diff options
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/jsruntime.pri | 33 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4calldata_p.h | 123 | ||||
-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.cpp | 108 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4compilationunitmapper_win.cpp | 128 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 809 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit_p.h | 324 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4staticvalue_p.h | 548 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4string_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4util_p.h | 196 |
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 |