aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/jsruntime.pri33
-rw-r--r--src/qml/jsruntime/qv4calldata_p.h123
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp (renamed from src/qml/jsruntime/qv4alloca_p.h)76
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h (renamed from src/qml/jsruntime/qv4stringtoarrayindex_p.h)55
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_unix.cpp108
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_win.cpp128
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp809
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h324
-rw-r--r--src/qml/jsruntime/qv4staticvalue_p.h548
-rw-r--r--src/qml/jsruntime/qv4string_p.h2
-rw-r--r--src/qml/jsruntime/qv4util_p.h196
11 files changed, 1419 insertions, 983 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 74e893e570..32acc6affc 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -1,7 +1,6 @@
INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
-!qmldevtools_build {
SOURCES += \
$$PWD/qv4engine.cpp \
$$PWD/qv4context.cpp \
@@ -60,13 +59,15 @@ SOURCES += \
$$PWD/qv4module.cpp \
$$PWD/qv4promiseobject.cpp \
$$PWD/qv4runtime.cpp \
- $$PWD/qv4value.cpp
+ $$PWD/qv4value.cpp \
+ $$PWD/qv4compilationunitmapper.cpp \
+ $$PWD/qv4executablecompilationunit.cpp \
+ $$PWD/qv4executableallocator.cpp
qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp
HEADERS += \
$$PWD/qv4global_p.h \
- $$PWD/qv4alloca_p.h \
$$PWD/qv4engine_p.h \
$$PWD/qv4enginebase_p.h \
$$PWD/qv4context_p.h \
@@ -133,7 +134,11 @@ HEADERS += \
$$PWD/qv4module_p.h \
$$PWD/qv4promiseobject_p.h \
$$PWD/qv4runtime_p.h \
- $$PWD/qv4value_p.h
+ $$PWD/qv4value_p.h \
+ $$PWD/qv4compilationunitmapper_p.h \
+ $$PWD/qv4executablecompilationunit_p.h \
+ $$PWD/qv4functiontable_p.h \
+ $$PWD/qv4runtimeapi_p.h
qtConfig(qml-sequence-object) {
HEADERS += \
@@ -143,24 +148,10 @@ qtConfig(qml-sequence-object) {
$$PWD/qv4sequenceobject.cpp
}
-}
-
+unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
+else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
-HEADERS += \
- $$PWD/qv4calldata_p.h \
- $$PWD/qv4runtimeapi_p.h \
- $$PWD/qv4stringtoarrayindex_p.h \
- $$PWD/qv4util_p.h \
- $$PWD/qv4functiontable_p.h \
- $$PWD/qv4staticvalue_p.h
-
-SOURCES += \
- $$PWD/qv4executableallocator.cpp
-
-qmldevtools_build {
- SOURCES += \
- $$PWD/qv4functiontable_noop.cpp
-} else:win32 {
+win32 {
!winrt:equals(QT_ARCH, x86_64) {
SOURCES += \
$$PWD/qv4functiontable_win64.cpp
diff --git a/src/qml/jsruntime/qv4calldata_p.h b/src/qml/jsruntime/qv4calldata_p.h
deleted file mode 100644
index 5a5280cb86..0000000000
--- a/src/qml/jsruntime/qv4calldata_p.h
+++ /dev/null
@@ -1,123 +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 QV4CALLDATA_P_H
-#define QV4CALLDATA_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 <private/qv4staticvalue_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-struct CallData
-{
- enum Offsets {
- Function = 0,
- Context = 1,
- Accumulator = 2,
- This = 3,
- NewTarget = 4,
- Argc = 5,
-
- LastOffset = Argc,
- OffsetCount = LastOffset + 1
- };
-
- StaticValue function;
- StaticValue context;
- StaticValue accumulator;
- StaticValue thisObject;
- StaticValue newTarget;
- StaticValue _argc;
-
- int argc() const {
- Q_ASSERT(_argc.isInteger());
- return _argc.int_32();
- }
-
- void setArgc(int argc) {
- Q_ASSERT(argc >= 0);
- _argc.setInt_32(argc);
- }
-
- inline ReturnedValue argument(int i) const {
- return i < argc() ? args[i].asReturnedValue()
- : StaticValue::undefinedValue().asReturnedValue();
- }
-
- StaticValue args[1];
-
- static Q_DECL_CONSTEXPR int HeaderSize()
- {
- return offsetof(CallData, args) / sizeof(QV4::StaticValue);
- }
-
- template<typename Value>
- Value *argValues();
-
- template<typename Value>
- const Value *argValues() const;
-};
-
-Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
-Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(StaticValue));
-Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(StaticValue));
-Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(StaticValue));
-Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(StaticValue));
-Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(StaticValue));
-Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(StaticValue));
-Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(StaticValue));
-
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4CALLDATA_P_H
diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index 65c3e4d65a..350f6f9485 100644
--- a/src/qml/jsruntime/qv4alloca_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -37,72 +37,26 @@
**
****************************************************************************/
-#ifndef QV4_ALLOCA_H
-#define QV4_ALLOCA_H
+#include "qv4compilationunitmapper_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 "qv4compileddata_p.h"
+#include <QFileInfo>
+#include <QDateTime>
+#include <QCoreApplication>
-#include <QtCore/private/qglobal_p.h>
-
-#if QT_CONFIG(alloca_h)
-# include <alloca.h>
-#elif QT_CONFIG(alloca_malloc_h)
-# include <malloc.h>
-// This does not matter unless compiling in strict standard mode.
-# ifdef Q_CC_MSVC
-# define alloca _alloca
-# endif
-#else
-# include <stdlib.h>
-#endif
-
-// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing
-// the occurrences of alloca() in case it's not supported.
-// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate
-// memory allocation from the declaration and RAII.
-#define Q_ALLOCA_VAR(type, name, size) \
- Q_ALLOCA_DECLARE(type, name); \
- Q_ALLOCA_ASSIGN(type, name, size)
-
-#if QT_CONFIG(alloca)
-
-#define Q_ALLOCA_DECLARE(type, name) \
- type *name = 0
+QT_BEGIN_NAMESPACE
-#define Q_ALLOCA_ASSIGN(type, name, size) \
- name = static_cast<type*>(alloca(size))
+using namespace QV4;
-#else
-QT_BEGIN_NAMESPACE
-class Qt_AllocaWrapper
+CompilationUnitMapper::CompilationUnitMapper()
+ : dataPtr(nullptr)
{
-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
-
-#define Q_ALLOCA_DECLARE(type, name) \
- Qt_AllocaWrapper _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())
+}
-#endif
+CompilationUnitMapper::~CompilationUnitMapper()
+{
+ close();
+}
-#endif
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4stringtoarrayindex_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 61bd988d1e..80f914c141 100644
--- a/src/qml/jsruntime/qv4stringtoarrayindex_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 The Qt Company Ltd.
+** 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.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QV4STRINGTOARRAYINDEX_P_H
-#define QV4STRINGTOARRAYINDEX_P_H
+#ifndef QV4COMPILATIONUNITMAPPER_H
+#define QV4COMPILATIONUNITMAPPER_H
//
// W A R N I N G
@@ -51,46 +51,35 @@
// We mean it.
//
-#include <QtCore/private/qnumeric_p.h>
-#include <QtCore/qstring.h>
-#include <limits>
+#include <private/qv4global_p.h>
+#include <QFile>
QT_BEGIN_NAMESPACE
namespace QV4 {
-inline uint charToUInt(const QChar *ch) { return ch->unicode(); }
-inline uint charToUInt(const char *ch) { return static_cast<unsigned char>(*ch); }
+namespace CompiledData {
+struct Unit;
+}
-template <typename T>
-uint stringToArrayIndex(const T *ch, const T *end)
+class CompilationUnitMapper
{
- uint i = charToUInt(ch) - '0';
- if (i > 9)
- return std::numeric_limits<uint>::max();
- ++ch;
- // reject "01", "001", ...
- if (i == 0 && ch != end)
- return std::numeric_limits<uint>::max();
+public:
+ CompilationUnitMapper();
+ ~CompilationUnitMapper();
- while (ch < 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
- return std::numeric_limits<uint>::max();
- ++ch;
- }
- return i;
-}
+ CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString);
+ void close();
-inline uint stringToArrayIndex(const QString &str)
-{
- return stringToArrayIndex(str.constData(), str.constData() + str.length());
-}
+private:
+#if defined(Q_OS_UNIX)
+ size_t length;
+#endif
+ void *dataPtr;
+};
-} // namespace QV4
+}
QT_END_NAMESPACE
-#endif // QV4STRINGTOARRAYINDEX_P_H
+#endif // QV4COMPILATIONUNITMAPPER_H
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
new file mode 100644
index 0000000000..6768bc9596
--- /dev/null
+++ b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qv4compilationunitmapper_p.h"
+
+#include <sys/mman.h>
+#include <functional>
+#include <private/qcore_unix_p.h>
+#include <QScopeGuard>
+#include <QDateTime>
+
+#include "qv4compileddata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString)
+{
+ close();
+
+ int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY);
+ if (fd == -1) {
+ *errorString = qt_error_string(errno);
+ return nullptr;
+ }
+
+ auto cleanup = qScopeGuard([fd]{
+ qt_safe_close(fd) ;
+ });
+
+ CompiledData::Unit header;
+ qint64 bytesRead = qt_safe_read(fd, reinterpret_cast<char *>(&header), sizeof(header));
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return nullptr;
+ }
+
+ if (!header.verifyHeader(sourceTimeStamp, errorString))
+ return nullptr;
+
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
+ length = static_cast<size_t>(lseek(fd, 0, SEEK_END));
+
+ void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0);
+ if (ptr == MAP_FAILED) {
+ *errorString = qt_error_string(errno);
+ return nullptr;
+ }
+ dataPtr = ptr;
+
+ return reinterpret_cast<CompiledData::Unit*>(dataPtr);
+}
+
+void CompilationUnitMapper::close()
+{
+ // Do not unmap the data here.
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ munmap(dataPtr, length);
+ }
+ dataPtr = nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4compilationunitmapper_win.cpp b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
new file mode 100644
index 0000000000..779c1288fe
--- /dev/null
+++ b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qv4compilationunitmapper_p.h"
+
+#include "qv4compileddata_p.h"
+#include <QScopeGuard>
+#include <QFileInfo>
+#include <QDateTime>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString)
+{
+ close();
+
+ // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry
+ // is exported from QtCore.
+ HANDLE handle =
+#if defined(Q_OS_WINRT)
+ CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()),
+ GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
+ OPEN_EXISTING, nullptr);
+#else
+ CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()),
+ GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+#endif
+ if (handle == INVALID_HANDLE_VALUE) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ auto fileHandleCleanup = qScopeGuard([handle]{
+ CloseHandle(handle);
+ });
+
+ CompiledData::Unit header;
+ DWORD bytesRead;
+ if (!ReadFile(handle, reinterpret_cast<char *>(&header), sizeof(header), &bytesRead, nullptr)) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return nullptr;
+ }
+
+ if (!header.verifyHeader(sourceTimeStamp, errorString))
+ return nullptr;
+
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
+ HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0);
+ if (!fileMappingHandle) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ auto mappingCleanup = qScopeGuard([fileMappingHandle]{
+ CloseHandle(fileMappingHandle);
+ });
+
+ dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
+ if (!dataPtr) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ return reinterpret_cast<CompiledData::Unit*>(dataPtr);
+}
+
+void CompilationUnitMapper::close()
+{
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ UnmapViewOfFile(dataPtr);
+ }
+ dataPtr = nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
new file mode 100644
index 0000000000..c68f6a7cbf
--- /dev/null
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -0,0 +1,809 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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$
+**
+****************************************************************************/
+
+#include "qv4executablecompilationunit_p.h"
+
+#include <private/qv4engine_p.h>
+#include <private/qv4regexp_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4instr_moth_p.h>
+#include <private/qv4objectproto_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4module_p.h>
+#include <private/qv4compilationunitmapper_p.h>
+
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlpropertymap.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qscopeguard.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+ExecutableCompilationUnit::ExecutableCompilationUnit() = default;
+
+ExecutableCompilationUnit::ExecutableCompilationUnit(
+ CompiledData::CompilationUnit &&compilationUnit)
+ : CompiledData::CompilationUnit(std::move(compilationUnit))
+{}
+
+ExecutableCompilationUnit::~ExecutableCompilationUnit()
+{
+ unlink();
+}
+
+QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
+}
+
+static QString toString(QV4::ReturnedValue v)
+{
+ Value val = Value::fromReturnedValue(v);
+ QString result;
+ if (val.isInt32())
+ result = QLatin1String("int ");
+ else if (val.isDouble())
+ result = QLatin1String("double ");
+ if (val.isEmpty())
+ result += QLatin1String("empty");
+ else
+ result += val.toQStringNoThrow();
+ return result;
+}
+
+static void dumpConstantTable(const StaticValue *constants, uint count)
+{
+ QDebug d = qDebug();
+ d.nospace() << right;
+ for (uint i = 0; i < count; ++i) {
+ d << qSetFieldWidth(8) << i << qSetFieldWidth(0) << ": "
+ << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n";
+ }
+}
+
+QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
+{
+ this->engine = engine;
+ engine->compilationUnits.insert(this);
+
+ Q_ASSERT(!runtimeStrings);
+ Q_ASSERT(data);
+ const quint32 stringCount = totalStringCount();
+ runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*));
+ // memset the strings to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*));
+ for (uint i = 0; i < stringCount; ++i)
+ runtimeStrings[i] = engine->newString(stringAt(i));
+
+ runtimeRegularExpressions
+ = new QV4::Value[data->regexpTableSize];
+ // memset the regexps to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeRegularExpressions, 0,
+ data->regexpTableSize * sizeof(QV4::Value));
+ for (uint i = 0; i < data->regexpTableSize; ++i) {
+ const CompiledData::RegExp *re = data->regexpAt(i);
+ uint f = re->flags;
+ const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
+ runtimeRegularExpressions[i] = QV4::RegExp::create(
+ engine, stringAt(re->stringIndex), flags);
+ }
+
+ if (data->lookupTableSize) {
+ runtimeLookups = new QV4::Lookup[data->lookupTableSize];
+ memset(runtimeLookups, 0, data->lookupTableSize * sizeof(QV4::Lookup));
+ const CompiledData::Lookup *compiledLookups = data->lookupTable();
+ for (uint i = 0; i < data->lookupTableSize; ++i) {
+ QV4::Lookup *l = runtimeLookups + i;
+
+ CompiledData::Lookup::Type type
+ = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags));
+ if (type == CompiledData::Lookup::Type_Getter)
+ l->getter = QV4::Lookup::getterGeneric;
+ else if (type == CompiledData::Lookup::Type_Setter)
+ l->setter = QV4::Lookup::setterGeneric;
+ else if (type == CompiledData::Lookup::Type_GlobalGetter)
+ l->globalGetter = QV4::Lookup::globalGetterGeneric;
+ else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ l->nameIndex = compiledLookups[i].nameIndex;
+ }
+ }
+
+ if (data->jsClassTableSize) {
+ runtimeClasses
+ = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize
+ * sizeof(QV4::Heap::InternalClass *));
+ // memset the regexps to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeClasses, 0,
+ data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ for (uint i = 0; i < data->jsClassTableSize; ++i) {
+ int memberCount = 0;
+ const CompiledData::JSClassMember *member
+ = data->jsClassAt(i, &memberCount);
+ runtimeClasses[i]
+ = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
+ for (int j = 0; j < memberCount; ++j, ++member)
+ runtimeClasses[i]
+ = runtimeClasses[i]->addMember(
+ engine->identifierTable->asPropertyKey(
+ runtimeStrings[member->nameOffset]),
+ member->isAccessor
+ ? QV4::Attr_Accessor
+ : QV4::Attr_Data);
+ }
+ }
+
+ runtimeFunctions.resize(data->functionTableSize);
+ for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
+ const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
+ runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction);
+ }
+
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope);
+
+ runtimeBlocks.resize(data->blockTableSize);
+ for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
+ const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
+ ic = engine->internalClasses(EngineBase::Class_CallContext);
+
+ // first locals
+ const quint32_le *localsIndices = compiledBlock->localsTable();
+ for (quint32 j = 0; j < compiledBlock->nLocals; ++j)
+ ic = ic->addMember(
+ engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]),
+ Attr_NotConfigurable);
+ runtimeBlocks[i] = ic->d();
+ }
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Constant table";
+ dumpConstantTable(constants, data->constantTableSize);
+ qDebug() << "=== String table";
+ for (uint i = 0, end = totalStringCount(); i < end; ++i)
+ qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
+ qDebug() << "=== Closure table";
+ for (uint i = 0; i < data->functionTableSize; ++i)
+ qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
+ qDebug() << "root function at index "
+ << (data->indexOfRootFunction != -1
+ ? data->indexOfRootFunction : 0);
+ }
+
+ if (data->indexOfRootFunction != -1)
+ return runtimeFunctions[data->indexOfRootFunction];
+ else
+ return nullptr;
+}
+
+Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
+{
+ Q_ASSERT(index < int(data->templateObjectTableSize));
+ if (!templateObjects.size())
+ templateObjects.resize(data->templateObjectTableSize);
+ Heap::Object *o = templateObjects.at(index);
+ if (o)
+ return o;
+
+ // create the template object
+ Scope scope(engine);
+ const CompiledData::TemplateObject *t = data->templateObjectAt(index);
+ Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size));
+ Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size));
+ ScopedValue s(scope);
+ for (uint i = 0; i < t->size; ++i) {
+ s = runtimeStrings[t->stringIndexAt(i)];
+ a->arraySet(i, s);
+ s = runtimeStrings[t->rawStringIndexAt(i)];
+ raw->arraySet(i, s);
+ }
+
+ ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1);
+ a->defineReadonlyProperty(QStringLiteral("raw"), raw);
+ ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1);
+
+ templateObjects[index] = a->objectValue()->d();
+ return templateObjects.at(index);
+}
+
+void ExecutableCompilationUnit::unlink()
+{
+ if (engine)
+ nextCompilationUnit.remove();
+
+ if (isRegisteredWithEngine) {
+ Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0));
+ if (qmlEngine)
+ qmlEngine->unregisterInternalCompositeType(this);
+ QQmlMetaType::unregisterInternalCompositeType(this);
+ isRegisteredWithEngine = false;
+ }
+
+ propertyCaches.clear();
+
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i) {
+ QV4::Lookup &l = runtimeLookups[i];
+ if (l.getter == QV4::QObjectWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
+ pc->release();
+ } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache)
+ pc->release();
+ }
+
+ if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) {
+ if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
+ pc->release();
+ }
+ }
+ }
+
+ dependentScripts.clear();
+
+ typeNameCache = nullptr;
+
+ qDeleteAll(resolvedTypes);
+ resolvedTypes.clear();
+
+ engine = nullptr;
+ qmlEngine = nullptr;
+
+ delete [] runtimeLookups;
+ runtimeLookups = nullptr;
+
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ f->destroy();
+ runtimeFunctions.clear();
+
+ CompiledData::CompilationUnit::unlink();
+}
+
+void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack)
+{
+ if (runtimeStrings) {
+ for (uint i = 0, end = totalStringCount(); i < end; ++i)
+ if (runtimeStrings[i])
+ runtimeStrings[i]->mark(markStack);
+ }
+ if (runtimeRegularExpressions) {
+ for (uint i = 0; i < data->regexpTableSize; ++i)
+ Value::fromStaticValue(runtimeRegularExpressions[i]).mark(markStack);
+ }
+ if (runtimeClasses) {
+ for (uint i = 0; i < data->jsClassTableSize; ++i)
+ if (runtimeClasses[i])
+ runtimeClasses[i]->mark(markStack);
+ }
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ if (f && f->internalClass)
+ f->internalClass->mark(markStack);
+ for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks))
+ if (c)
+ c->mark(markStack);
+
+ for (QV4::Heap::Object *o : qAsConst(templateObjects))
+ if (o)
+ o->mark(markStack);
+
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].markObjects(markStack);
+ }
+
+ if (auto mod = module())
+ mod->mark(markStack);
+}
+
+IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
+{
+ IdentifierHash namedObjectCache(engine);
+ const CompiledData::Object *component = objectAt(componentObjectIndex);
+ const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
+ for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
+ const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ }
+ return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
+}
+
+void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
+{
+ this->qmlEngine = qmlEngine;
+
+ // Add to type registry of composites
+ if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
+ QQmlMetaType::registerInternalCompositeType(this);
+ qmlEngine->registerInternalCompositeType(this);
+ } else {
+ const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
+ auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ if (typeRef->compilationUnit) {
+ metaTypeId = typeRef->compilationUnit->metaTypeId;
+ listMetaTypeId = typeRef->compilationUnit->listMetaTypeId;
+ } else {
+ metaTypeId = typeRef->type.typeId();
+ listMetaTypeId = typeRef->type.qListTypeId();
+ }
+ }
+
+ // Collect some data for instantiation later.
+ 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);
+ bindingCount += obj->nBindings;
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ if (typeRef->type.isValid()) {
+ if (typeRef->type.parserStatusCast() != -1)
+ ++parserStatusCount;
+ }
+ ++objectCount;
+ if (typeRef->compilationUnit) {
+ bindingCount += typeRef->compilationUnit->totalBindingsCount;
+ parserStatusCount += typeRef->compilationUnit->totalParserStatusCount;
+ objectCount += typeRef->compilationUnit->totalObjectCount;
+ }
+ }
+ }
+
+ totalBindingsCount = bindingCount;
+ totalParserStatusCount = parserStatusCount;
+ totalObjectCount = objectCount;
+}
+
+bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::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;
+}
+
+QStringList ExecutableCompilationUnit::moduleRequests() const
+{
+ QStringList requests;
+ requests.reserve(data->moduleRequestTableSize);
+ for (uint i = 0; i < data->moduleRequestTableSize; ++i)
+ requests << stringAt(data->moduleRequestTable()[i]);
+ return requests;
+}
+
+Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
+{
+ if (isESModule() && module())
+ return module();
+
+ if (data->indexOfRootFunction < 0)
+ return nullptr;
+
+ if (!this->engine)
+ linkToEngine(engine);
+
+ Scope scope(engine);
+ Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this));
+
+ if (isESModule())
+ setModule(module->d());
+
+ for (const QString &request: moduleRequests()) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
+ if (engine->hasException)
+ return nullptr;
+ dependentModuleUnit->instantiate(engine);
+ }
+
+ ScopedString importName(scope);
+
+ const uint importCount = data->importEntryTableSize;
+ if (importCount > 0) {
+ imports = new const StaticValue *[importCount];
+ memset(imports, 0, importCount * sizeof(StaticValue *));
+ }
+ for (uint i = 0; i < importCount; ++i) {
+ const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
+ importName = runtimeStrings[entry.importName];
+ const Value *valuePtr = dependentModuleUnit->resolveExport(importName);
+ if (!valuePtr) {
+ QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
+ referenceErrorMessage += importName->toQString();
+ engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ return nullptr;
+ }
+ imports[i] = valuePtr;
+ }
+
+ for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+
+ ScopedString importName(scope, runtimeStrings[entry.importName]);
+ if (!dependentModuleUnit->resolveExport(importName)) {
+ QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
+ referenceErrorMessage += importName->toQString();
+ engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ return nullptr;
+ }
+ }
+
+ return module->d();
+}
+
+const Value *ExecutableCompilationUnit::resolveExportRecursively(
+ QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
+{
+ if (!module())
+ return nullptr;
+
+ for (const auto &entry: *resolveSet)
+ if (entry.module == this && entry.exportName->isEqualTo(exportName))
+ return nullptr;
+
+ (*resolveSet) << ResolveSetEntry(this, exportName);
+
+ if (exportName->toQString() == QLatin1String("*"))
+ return &module()->self;
+
+ Scope scope(engine);
+
+ if (auto localExport = lookupNameInExportTable(
+ data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) {
+ ScopedString localName(scope, runtimeStrings[localExport->localName]);
+ uint index = module()->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey());
+ if (index == UINT_MAX)
+ return nullptr;
+ if (index >= module()->scope->locals.size)
+ return &(imports[index - module()->scope->locals.size]->asValue<Value>());
+ return &module()->scope->locals[index];
+ }
+
+ if (auto indirectExport = lookupNameInExportTable(
+ data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
+ auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+ ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
+ return dependentModuleUnit->resolveExportRecursively(importName, resolveSet);
+ }
+
+
+ if (exportName->toQString() == QLatin1String("default"))
+ return nullptr;
+
+ const Value *starResolution = nullptr;
+
+ for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+
+ const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet);
+ // ### handle ambiguous
+ if (resolution) {
+ if (!starResolution) {
+ starResolution = resolution;
+ continue;
+ }
+ if (resolution != starResolution)
+ return nullptr;
+ }
+ }
+
+ return starResolution;
+}
+
+const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable(
+ const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
+{
+ const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
+ auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
+ return stringAt(lhs.exportName) < name->toQString();
+ });
+ if (matchingExport == lastExportEntry || stringAt(matchingExport->exportName) != name->toQString())
+ return nullptr;
+ return matchingExport;
+}
+
+void ExecutableCompilationUnit::getExportedNamesRecursively(
+ QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet,
+ bool includeDefaultExport) const
+{
+ if (exportNameSet->contains(this))
+ return;
+ exportNameSet->append(this);
+
+ const auto append = [names, includeDefaultExport](const QString &name) {
+ if (!includeDefaultExport && name == QLatin1String("default"))
+ return;
+ names->append(name);
+ };
+
+ for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
+ append(stringAt(entry.exportName));
+ }
+
+ for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
+ append(stringAt(entry.exportName));
+ }
+
+ for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
+ if (!dependentModuleUnit)
+ return;
+ dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false);
+ }
+}
+
+void ExecutableCompilationUnit::evaluate()
+{
+ QV4::Scope scope(engine);
+ QV4::Scoped<Module> mod(scope, module());
+ mod->evaluate();
+}
+
+void ExecutableCompilationUnit::evaluateModuleRequests()
+{
+ for (const QString &request: moduleRequests()) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
+ if (engine->hasException)
+ return;
+ dependentModuleUnit->evaluate();
+ if (engine->hasException)
+ return;
+ }
+}
+
+bool ExecutableCompilationUnit::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);
+ QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
+
+ const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
+ for (const QString &cachePath : cachePaths) {
+ CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString);
+ if (!mappedUnit)
+ continue;
+
+ const CompiledData::Unit * const oldDataPtr
+ = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data
+ : nullptr;
+ const CompiledData::Unit *oldData = data;
+ auto dataPtrRevert = qScopeGuard([this, oldData](){
+ setUnitData(oldData);
+ });
+ setUnitData(mappedUnit);
+
+ if (data->sourceFileIndex != 0
+ && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
+ *errorString = QStringLiteral("QML source file has moved to a different location.");
+ continue;
+ }
+
+ dataPtrRevert.dismiss();
+ free(const_cast<CompiledData::Unit*>(oldDataPtr));
+ backingFile.reset(cacheFile.take());
+ return true;
+ }
+
+ return false;
+}
+
+bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
+{
+ if (data->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 CompilationUnit::saveToDisk(localCacheFilePath(unitUrl), errorString);
+}
+
+/*!
+Returns the property cache, if one alread exists. The cache is not referenced.
+*/
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const
+{
+ if (type.isValid())
+ return typePropertyCache;
+ else
+ return compilationUnit->rootPropertyCache();
+}
+
+/*!
+Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
+*/
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+{
+ if (typePropertyCache) {
+ return typePropertyCache;
+ } else if (type.isValid()) {
+ typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion);
+ return typePropertyCache;
+ } else {
+ return compilationUnit->rootPropertyCache();
+ }
+}
+
+bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine)
+{
+ if (type.isValid()) {
+ bool ok = false;
+ hash->addData(createPropertyCache(engine)->checksum(&ok));
+ return ok;
+ }
+ if (!compilationUnit)
+ return false;
+ hash->addData(compilationUnit->data->md5Checksum,
+ sizeof(compilationUnit->data->md5Checksum));
+ return true;
+}
+
+template <typename T>
+bool qtTypeInherits(const QMetaObject *mo) {
+ while (mo) {
+ if (mo == &T::staticMetaObject)
+ return true;
+ mo = mo->superClass();
+ }
+ return false;
+}
+
+void ResolvedTypeReference::doDynamicTypeCheck()
+{
+ const QMetaObject *mo = nullptr;
+ if (typePropertyCache)
+ mo = typePropertyCache->firstCppMetaObject();
+ else if (type.isValid())
+ mo = type.metaObject();
+ else if (compilationUnit)
+ mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo);
+}
+
+bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const
+{
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ if (!it.value()->addToHash(hash, engine))
+ return false;
+ }
+
+ return true;
+}
+
+QString ExecutableCompilationUnit::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));
+ case Binding::Type_Invalid:
+ return QString();
+#if !QT_CONFIG(translation)
+ case Binding::Type_TranslationById:
+ case Binding::Type_Translation:
+ return unit->stringAt(
+ unit->data->translations()[binding->value.translationDataIndex].stringIndex);
+#else
+ case Binding::Type_TranslationById: {
+ const TranslationData &translation
+ = data->translations()[binding->value.translationDataIndex];
+ QByteArray id = stringAt(translation.stringIndex).toUtf8();
+ return qtTrId(id.constData(), translation.number);
+ }
+ case Binding::Type_Translation: {
+ const TranslationData &translation
+ = data->translations()[binding->value.translationDataIndex];
+ // This code must match that in the qsTr() implementation
+ const QString &path = fileName();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5)
+ : QStringRef();
+ QByteArray contextUtf8 = context.toUtf8();
+ QByteArray comment = stringAt(translation.commentIndex).toUtf8();
+ QByteArray text = stringAt(translation.stringIndex).toUtf8();
+ return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
+ comment.constData(), translation.number);
+ }
+#endif
+ default:
+ break;
+ }
+ return QString();
+}
+
+QString ExecutableCompilationUnit::bindingValueAsScriptString(
+ const CompiledData::Binding *binding) const
+{
+ return (binding->type == CompiledData::Binding::Type_String)
+ ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex))
+ : bindingValueAsString(binding);
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
new file mode 100644
index 0000000000..4e3aadf28a
--- /dev/null
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 QV4EXECUTABLECOMPILATIONUNIT_P_H
+#define QV4EXECUTABLECOMPILATIONUNIT_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 <private/qv4compileddata_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qintrusivelist_p.h>
+#include <private/qqmlpropertycachevector_p.h>
+#include <private/qqmltype_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEnginePrivate;
+namespace QV4 {
+
+class CompilationUnitMapper;
+struct ResolvedTypeReference;
+// map from name index
+// While this could be a hash, a map is chosen here to provide a stable
+// order, which is used to calculating a check-sum on dependent meta-objects.
+struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*>
+{
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const;
+};
+
+class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit,
+ public QQmlRefCount
+{
+ Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit)
+public:
+ friend class QQmlRefPointer<ExecutableCompilationUnit>;
+
+ static QQmlRefPointer<ExecutableCompilationUnit> create(
+ CompiledData::CompilationUnit &&compilationUnit)
+ {
+ return QQmlRefPointer<ExecutableCompilationUnit>(
+ new ExecutableCompilationUnit(std::move(compilationUnit)),
+ QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
+ }
+
+ static QQmlRefPointer<ExecutableCompilationUnit> create()
+ {
+ return QQmlRefPointer<ExecutableCompilationUnit>(
+ new ExecutableCompilationUnit,
+ QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
+ }
+
+ QIntrusiveListNode nextCompilationUnit;
+ ExecutionEngine *engine = nullptr;
+ QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case.
+
+ // 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.isNull) m_url = QUrl(fileName()); return m_url; }
+ QUrl finalUrl() const
+ {
+ if (m_finalUrl.isNull)
+ m_finalUrl = QUrl(finalUrlString());
+ return m_finalUrl;
+ }
+
+ QV4::Lookup *runtimeLookups = nullptr;
+ QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
+ mutable QVector<QV4::Heap::Object *> templateObjects;
+ mutable QQmlNullableValue<QUrl> m_url;
+ mutable QQmlNullableValue<QUrl> m_finalUrl;
+
+ // QML specific fields
+ QQmlPropertyCacheVector propertyCaches;
+ QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
+
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+
+ // index is object index. This allows fast access to the
+ // property data when initializing bindings, avoiding expensive
+ // lookups by string (property name).
+ QVector<CompiledData::BindingPropertyData> bindingPropertyDataPerObject;
+
+ // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
+ // this is initialized on-demand by QQmlContextData
+ QHash<int, IdentifierHash> namedObjectsPerComponentCache;
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
+
+ void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
+
+ int totalBindingsCount = 0; // Number of bindings used in this type
+ int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
+ int totalObjectCount = 0; // Number of objects explicitly instantiated
+
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
+ ResolvedTypeReferenceMap resolvedTypes;
+ ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
+
+ bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const;
+
+ int metaTypeId = -1;
+ int listMetaTypeId = -1;
+ bool isRegisteredWithEngine = false;
+
+ QScopedPointer<CompilationUnitMapper> backingFile;
+
+ // --- interface for QQmlPropertyCacheCreator
+ using CompiledObject = CompiledData::Object;
+ using CompiledFunction = CompiledData::Function;
+
+ int objectCount() const { return qmlData->nObjects; }
+ const CompiledObject *objectAt(int index) const
+ {
+ return qmlData->objectAt(index);
+ }
+
+ int importCount() const { return qmlData->nImports; }
+ const CompiledData::Import *importAt(int index) const
+ {
+ return qmlData->importAt(index);
+ }
+
+ Heap::Object *templateObjectAt(int index) const;
+
+ 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(data, object, 0);
+ }
+
+ FunctionIterator objectFunctionsEnd(const CompiledObject *object) const
+ {
+ return FunctionIterator(data, object, object->nFunctions);
+ }
+
+ bool isESModule() const
+ {
+ return data->flags & CompiledData::Unit::IsESModule;
+ }
+
+ bool isSharedLibrary() const
+ {
+ return data->flags & CompiledData::Unit::IsSharedLibrary;
+ }
+
+ QStringList moduleRequests() const;
+ Heap::Module *instantiate(ExecutionEngine *engine);
+ const Value *resolveExport(QV4::String *exportName)
+ {
+ QVector<ResolveSetEntry> resolveSet;
+ return resolveExportRecursively(exportName, &resolveSet);
+ }
+
+ QStringList exportedNames() const
+ {
+ QStringList names;
+ QVector<const ExecutableCompilationUnit*> exportNameSet;
+ getExportedNamesRecursively(&names, &exportNameSet);
+ names.sort();
+ auto last = std::unique(names.begin(), names.end());
+ names.erase(last, names.end());
+ return names;
+ }
+
+ void evaluate();
+ void evaluateModuleRequests();
+
+ QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
+ void unlink();
+
+ void markObjects(MarkStack *markStack);
+
+ bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+
+ static QString localCacheFilePath(const QUrl &url);
+ bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+
+ QString bindingValueAsString(const CompiledData::Binding *binding) const;
+ QString bindingValueAsScriptString(const CompiledData::Binding *binding) const;
+ double bindingValueAsNumber(const CompiledData::Binding *binding) const
+ {
+ if (binding->type != CompiledData::Binding::Type_Number)
+ return 0.0;
+ return constants[binding->value.constantValueIndex].doubleValue();
+ }
+
+protected:
+ quint32 totalStringCount() const
+ { return data->stringTableSize; }
+
+private:
+ struct ResolveSetEntry
+ {
+ ResolveSetEntry() {}
+ ResolveSetEntry(ExecutableCompilationUnit *module, QV4::String *exportName)
+ : module(module), exportName(exportName) {}
+ ExecutableCompilationUnit *module = nullptr;
+ QV4::String *exportName = nullptr;
+ };
+
+ ExecutableCompilationUnit();
+ ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit);
+ ~ExecutableCompilationUnit();
+
+ const Value *resolveExportRecursively(QV4::String *exportName,
+ QVector<ResolveSetEntry> *resolveSet);
+
+ QUrl urlAt(int index) const { return QUrl(stringAt(index)); }
+
+ Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
+ const CompiledData::ExportEntry *lookupNameInExportTable(
+ const CompiledData::ExportEntry *firstExportEntry, int tableSize,
+ QV4::String *name) const;
+
+ void getExportedNamesRecursively(
+ QStringList *names, QVector<const ExecutableCompilationUnit *> *exportNameSet,
+ bool includeDefaultExport = true) const;
+};
+
+struct ResolvedTypeReference
+{
+ ResolvedTypeReference()
+ : majorVersion(0)
+ , minorVersion(0)
+ , isFullyDynamicType(false)
+ {}
+
+ QQmlType type;
+ QQmlRefPointer<QQmlPropertyCache> typePropertyCache;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
+
+ int majorVersion;
+ int minorVersion;
+ // Types such as QQmlPropertyMap can add properties dynamically at run-time and
+ // therefore cannot have a property cache installed when instantiated.
+ bool isFullyDynamicType;
+
+ QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
+ QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
+
+ void doDynamicTypeCheck();
+};
+
+IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ return createNamedObjectsPerComponent(componentObjectIndex);
+ return *it;
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4EXECUTABLECOMPILATIONUNIT_P_H
diff --git a/src/qml/jsruntime/qv4staticvalue_p.h b/src/qml/jsruntime/qv4staticvalue_p.h
deleted file mode 100644
index c6b4bdb158..0000000000
--- a/src/qml/jsruntime/qv4staticvalue_p.h
+++ /dev/null
@@ -1,548 +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 QV4STATICVALUE_P_H
-#define QV4STATICVALUE_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 <QtQml/private/qtqmlglobal_p.h>
-#include <QtQml/private/qv4global_p.h>
-#include <QtCore/private/qnumeric_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-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);
- }
- }
-};
-
-struct Q_QML_PRIVATE_EXPORT StaticValue
-{
- StaticValue() = default;
- constexpr StaticValue(quint64 val) : _val(val) {}
-
- StaticValue &operator=(ReturnedValue v)
- {
- _val = v;
- return *this;
- }
-
- template<typename Value>
- StaticValue &operator=(const Value &);
-
- template<typename Value>
- const Value &asValue() const;
-
- template<typename Value>
- 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.
-
- Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
-
- Specific bit-sequences:
- 0 = always 0
- 1 = always 1
- x = stored value
- a,b,c,d = specific bit values, see notes
-
- 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.
- */
-
- quint64 _val;
-
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; }
-
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- static inline int valueOffset() { return 0; }
- static inline int tagOffset() { return 4; }
-#else // !Q_LITTLE_ENDIAN
- 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; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
- QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
- QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); }
-
- QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const
- {
- return int(value());
- }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i)
- {
- setTagValue(quint32(ValueTypeInternal::Integer), quint32(i));
- }
- QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
-
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_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
- };
-
- 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);
- }
-
- // Shared between 32-bit and 64-bit encoding
- enum {
- Tag_Shift = 32
- };
-
- // 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
-
- enum class ValueTypeInternal_64 {
- Empty = Immediate_Mask_64 | 0,
- Null = Immediate_Mask_64 | 0x08000u,
- Boolean = Immediate_Mask_64 | 0x10000u,
- Integer = Immediate_Mask_64 | 0x18000u
- };
-
- // Used only by 32-bit encoding
- enum Masks {
- SilentNaNBit = 0x00040000,
- NotDouble_Mask = 0x7ffa0000,
- };
- 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
- };
-
- enum {
- Managed_Type_Internal = 0
- };
-
- using ValueTypeInternal = ValueTypeInternal_64;
-
- enum {
- NaN_Mask = 0x7ff80000,
- };
-
- inline quint64 quickType() const { return (_val >> QuickType_Shift); }
-
- // used internally in property
- inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); }
- inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); }
- 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
- {
-#if QT_POINTER_SIZE == 4
- return value() && tag() == Managed_Type_Internal;
-#else
- return _val && ((_val >> IsManagedOrUndefined_Shift) == 0);
-#endif
- }
- inline bool isManagedOrUndefined() const
- {
-#if QT_POINTER_SIZE == 4
- return tag() == Managed_Type_Internal;
-#else
- return ((_val >> IsManagedOrUndefined_Shift) == 0);
-#endif
- }
-
- inline bool isIntOrBool() const {
- return (_val >> IsIntegerOrBool_Shift) == 3;
- }
-
- inline bool integerCompatible() const {
- Q_ASSERT(!isEmpty());
- return (_val >> IsIntegerConvertible_Shift) == 1;
- }
-
- static inline bool integerCompatible(StaticValue a, StaticValue b) {
- return a.integerCompatible() && b.integerCompatible();
- }
-
- static inline bool bothDouble(StaticValue a, StaticValue b) {
- return a.isDouble() && b.isDouble();
- }
-
- inline bool isNaN() const
- {
- return (tag() & 0x7ffc0000 ) == 0x00040000;
- }
-
- inline bool isPositiveInt() const {
-#if QT_POINTER_SIZE == 4
- return isInteger() && int_32() >= 0;
-#else
- return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
-#endif
- }
-
- QML_NEARLY_ALWAYS_INLINE double doubleValue() const {
- Q_ASSERT(isDouble());
- double d;
- StaticValue v = *this;
- v._val ^= NaNEncodeMask;
- memcpy(&d, &v._val, 8);
- return d;
- }
-
- QML_NEARLY_ALWAYS_INLINE void setDouble(double d) {
- if (qt_is_nan(d))
- d = qt_qnan();
- memcpy(&_val, &d, 8);
- _val ^= NaNEncodeMask;
- Q_ASSERT(isDouble());
- }
-
- inline bool isInt32() {
- if (tag() == quint32(ValueTypeInternal::Integer))
- return true;
- if (isDouble()) {
- double d = doubleValue();
- if (isInt32(d)) {
- setInt_32(int(d));
- return true;
- }
- }
- return false;
- }
-
- QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
- int i = int(d);
- return (i == d && !(d == 0 && std::signbit(d)));
- }
-
- double asDouble() const {
- if (tag() == quint32(ValueTypeInternal::Integer))
- return int_32();
- return doubleValue();
- }
-
- bool booleanValue() const {
- return int_32();
- }
-
- int integerValue() const {
- return int_32();
- }
-
- inline bool tryIntegerConversion() {
- bool b = integerCompatible();
- if (b)
- setTagValue(quint32(ValueTypeInternal::Integer), value());
- return b;
- }
-
- bool toBoolean() const {
- if (integerCompatible())
- return static_cast<bool>(int_32());
-
- if (isManagedOrUndefined())
- return false;
-
- // double
- const double d = doubleValue();
- return d && !std::isnan(d);
- }
-
- inline int toInt32() const
- {
- switch (type()) {
- case Null_Type:
- case Boolean_Type:
- case Integer_Type:
- return int_32();
- case Double_Type:
- return Double::toInt32(doubleValue());
- case Empty_Type:
- case Undefined_Type:
- case Managed_Type:
- break;
- }
- return Double::toInt32(std::numeric_limits<double>::quiet_NaN());
- }
-
- 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 undefinedValue() { return { 0 }; }
- static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; }
-
- static inline StaticValue fromDouble(double d)
- {
- StaticValue v;
- v.setDouble(d);
- return v;
- }
-
- static inline StaticValue fromUInt32(uint i)
- {
- StaticValue v;
- if (i < uint(std::numeric_limits<int>::max())) {
- v.setTagValue(quint32(ValueTypeInternal::Integer), i);
- } else {
- v.setDouble(i);
- }
- return v;
- }
-
- static double toInteger(double d)
- {
- if (std::isnan(d))
- return +0;
- if (!d || std::isinf(d))
- return d;
- return d >= 0 ? std::floor(d) : std::ceil(d);
- }
-
- static int toInt32(double d)
- {
- return Double::toInt32(d);
- }
-
- static unsigned int toUInt32(double d)
- {
- return static_cast<uint>(toInt32(d));
- }
-};
-Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value);
-
-struct Encode {
- static constexpr ReturnedValue undefined() {
- return StaticValue::undefinedValue().asReturnedValue();
- }
- static constexpr ReturnedValue null() {
- return StaticValue::nullValue().asReturnedValue();
- }
-
- explicit constexpr Encode(bool b)
- : val(StaticValue::fromBoolean(b).asReturnedValue())
- {
- }
- explicit Encode(double d) {
- val = StaticValue::fromDouble(d).asReturnedValue();
- }
- explicit constexpr Encode(int i)
- : val(StaticValue::fromInt32(i).asReturnedValue())
- {
- }
- explicit Encode(uint i) {
- val = StaticValue::fromUInt32(i).asReturnedValue();
- }
- explicit constexpr Encode(ReturnedValue v)
- : val(v)
- {
- }
- constexpr Encode(StaticValue v)
- : val(v.asReturnedValue())
- {
- }
-
- template<typename HeapBase>
- explicit Encode(HeapBase *o);
-
- explicit Encode(StaticValue *o) {
- Q_ASSERT(o);
- val = o->asReturnedValue();
- }
-
- static ReturnedValue smallestNumber(double d) {
- if (StaticValue::isInt32(d))
- return Encode(static_cast<int>(d));
- else
- return Encode(d);
- }
-
- constexpr operator ReturnedValue() const {
- return val;
- }
- quint64 val;
-private:
- explicit Encode(void *);
-};
-
-}
-
-QT_END_NAMESPACE
-
-#endif // QV4STATICVALUE_P_H
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 7888809490..52fe09cd72 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -54,7 +54,7 @@
#include "qv4managed_p.h"
#include <QtCore/private/qnumeric_p.h>
#include "qv4enginebase_p.h"
-#include "qv4stringtoarrayindex_p.h"
+#include <private/qv4stringtoarrayindex_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h
deleted file mode 100644
index 073832937d..0000000000
--- a/src/qml/jsruntime/qv4util_p.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#ifndef QV4UTIL_H
-#define QV4UTIL_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 "qv4global_p.h"
-#include <QtCore/QBitArray>
-#include <algorithm>
-#include <vector>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND)
-// Sanity:
-class BitVector
-{
- std::vector<bool> bits;
-
-public:
- BitVector(int size = 0, bool value = false)
- : bits(size, value)
- {}
-
- void clear()
- { bits = std::vector<bool>(bits.size(), false); }
-
- void reserve(int size)
- { bits.reserve(size); }
-
- int size() const
- {
- Q_ASSERT(bits.size() < INT_MAX);
- return static_cast<int>(bits.size());
- }
-
- void resize(int newSize)
- { bits.resize(newSize); }
-
- void resize(int newSize, bool newValue)
- { bits.resize(newSize, newValue); }
-
- void assign(int newSize, bool value)
- { bits.assign(newSize, value); }
-
- int findNext(int start, bool value, bool wrapAround) const
- {
- // The ++operator of std::vector<bool>::iterator in libc++ has a bug when using it on an
- // iterator pointing to the last element. It will not be set to ::end(), but beyond
- // that. (It will be set to the first multiple of the native word size that is bigger
- // than size().)
- //
- // See http://llvm.org/bugs/show_bug.cgi?id=19663
- //
- // The work-around is to calculate the distance, and compare it to the size() to see if it's
- // beyond the end, or take the minimum of the distance and the size.
-
- size_t pos = std::distance(bits.begin(),
- std::find(bits.begin() + start, bits.end(), value));
- if (wrapAround && pos >= static_cast<size_t>(size()))
- pos = std::distance(bits.begin(),
- std::find(bits.begin(), bits.begin() + start, value));
-
- pos = qMin(pos, static_cast<size_t>(size()));
-
- Q_ASSERT(pos <= static_cast<size_t>(size()));
- Q_ASSERT(pos < INT_MAX);
-
- return static_cast<int>(pos);
- }
-
- bool at(int idx) const
- { return bits.at(idx); }
-
- void setBit(int idx)
- { bits[idx] = true; }
-
- void clearBit(int idx)
- { bits[idx] = false; }
-};
-#else // Insanity:
-class BitVector
-{
- QBitArray bits;
-
-public:
- BitVector(int size = 0, bool value = false)
- : bits(size, value)
- {}
-
- void clear()
- { bits = QBitArray(bits.size(), false); }
-
- void reserve(int size)
- { Q_UNUSED(size); }
-
- int size() const
- { return bits.size(); }
-
- void resize(int newSize)
- { bits.resize(newSize); }
-
- void resize(int newSize, bool newValue)
- {
- int oldSize = bits.size();
- bits.resize(newSize);
- bits.fill(newValue, oldSize, bits.size());
- }
-
- void assign(int newSize, bool value)
- {
- bits.resize(newSize);
- bits.fill(value);
- }
-
- int findNext(int start, bool value, bool wrapAround) const
- {
- for (int i = start, ei = size(); i < ei; ++i) {
- if (at(i) == value)
- return i;
- }
-
- if (wrapAround) {
- for (int i = 0, ei = start; i < ei; ++i) {
- if (at(i) == value)
- return i;
- }
- }
-
- return size();
- }
-
- bool at(int idx) const
- { return bits.at(idx); }
-
- void setBit(int idx)
- { bits[idx] = true; }
-
- void clearBit(int idx)
- { bits[idx] = false; }
-};
-#endif
-
-}
-
-QT_END_NAMESPACE
-
-#endif // QV4UTIL_H