diff options
Diffstat (limited to 'src/qml/jsruntime')
79 files changed, 3474 insertions, 2786 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index a24ee0a188..32acc6affc 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -1,8 +1,8 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD -!qmldevtools_build { SOURCES += \ + $$PWD/qv4engine.cpp \ $$PWD/qv4context.cpp \ $$PWD/qv4persistent.cpp \ $$PWD/qv4lookup.cpp \ @@ -36,13 +36,13 @@ SOURCES += \ $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ $$PWD/qv4stackframe.cpp \ + $$PWD/qv4string.cpp \ $$PWD/qv4stringiterator.cpp \ $$PWD/qv4stringobject.cpp \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ $$PWD/qv4regexp.cpp \ $$PWD/qv4runtimecodegen.cpp \ - $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ $$PWD/qv4symbol.cpp \ $$PWD/qv4setobject.cpp \ @@ -57,13 +57,17 @@ SOURCES += \ $$PWD/qv4mapiterator.cpp \ $$PWD/qv4estable.cpp \ $$PWD/qv4module.cpp \ - $$PWD/qv4promiseobject.cpp + $$PWD/qv4promiseobject.cpp \ + $$PWD/qv4runtime.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 \ @@ -103,13 +107,13 @@ HEADERS += \ $$PWD/qv4regexpobject_p.h \ $$PWD/qv4runtimecodegen_p.h \ $$PWD/qv4stackframe_p.h \ + $$PWD/qv4string_p.h \ $$PWD/qv4stringiterator_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ $$PWD/qv4objectiterator_p.h \ $$PWD/qv4regexp_p.h \ - $$PWD/qv4serialize_p.h \ $$PWD/qv4script_p.h \ $$PWD/qv4symbol_p.h \ $$PWD/qv4setobject_p.h \ @@ -128,7 +132,13 @@ HEADERS += \ $$PWD/qv4estable_p.h \ $$PWD/qv4vtable_p.h \ $$PWD/qv4module_p.h \ - $$PWD/qv4promiseobject_p.h + $$PWD/qv4promiseobject_p.h \ + $$PWD/qv4runtime_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 += \ @@ -138,29 +148,10 @@ qtConfig(qml-sequence-object) { $$PWD/qv4sequenceobject.cpp } -} - - -HEADERS += \ - $$PWD/qv4runtime_p.h \ - $$PWD/qv4runtimeapi_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4string_p.h \ - $$PWD/qv4util_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4functiontable_p.h - -SOURCES += \ - $$PWD/qv4engine.cpp \ - $$PWD/qv4runtime.cpp \ - $$PWD/qv4string.cpp \ - $$PWD/qv4value.cpp \ - $$PWD/qv4executableallocator.cpp +unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp +else: SOURCES += $$PWD/qv4compilationunitmapper_win.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/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 4a21f62cf2..206e2b9aa4 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -38,13 +38,14 @@ ****************************************************************************/ #include <qv4argumentsobject_p.h> #include <qv4arrayobject_p.h> -#include <qv4alloca_p.h> #include <qv4scopedvalue_p.h> #include <qv4string_p.h> #include <qv4function_p.h> #include <qv4jscall_p.h> #include <qv4symbol_p.h> +#include <private/qv4alloca_p.h> + using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); @@ -116,6 +117,9 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); args->fullyCreate(); + if (!id.isArrayIndex()) + return Object::virtualDefineOwnProperty(m, id, desc, attrs); + uint index = id.asArrayIndex(); if (!args->isMapped(index)) @@ -148,36 +152,42 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { - const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - uint index = id.asArrayIndex(); - if (index < args->d()->argCount && !args->d()->fullyCreated) { - if (hasProperty) - *hasProperty = true; - return args->context()->args()[index].asReturnedValue(); + if (id.isArrayIndex()) { + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); + uint index = id.asArrayIndex(); + if (index < args->d()->argCount && !args->d()->fullyCreated) { + if (hasProperty) + *hasProperty = true; + return args->context()->args()[index].asReturnedValue(); + } + + if (args->isMapped(index)) { + Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount())); + if (hasProperty) + *hasProperty = true; + return args->context()->args()[index].asReturnedValue(); + } } - if (!args->isMapped(index)) - return Object::virtualGet(m, id, receiver, hasProperty); - Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount())); - if (hasProperty) - *hasProperty = true; - return args->context()->args()[index].asReturnedValue(); + return Object::virtualGet(m, id, receiver, hasProperty); } bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { - ArgumentsObject *args = static_cast<ArgumentsObject *>(m); - uint index = id.asArrayIndex(); - - if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) { - args->context()->setArg(index, value); - return true; + if (id.isArrayIndex()) { + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + uint index = id.asArrayIndex(); + + if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) { + args->context()->setArg(index, value); + return true; + } + + bool isMapped = (args == receiver && args->isMapped(index)); + if (isMapped) + args->context()->setArg(index, value); } - bool isMapped = (args == receiver && args->isMapped(index)); - if (isMapped) - args->context()->setArg(index, value); - return Object::virtualPut(m, id, value, receiver); } @@ -186,13 +196,16 @@ bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id) ArgumentsObject *args = static_cast<ArgumentsObject *>(m); args->fullyCreate(); bool result = Object::virtualDeleteProperty(m, id); - if (result) + if (result && id.isArrayIndex()) args->removeMapping(id.asArrayIndex()); return result; } PropertyAttributes ArgumentsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) { + if (!id.isArrayIndex()) + return Object::virtualGetOwnProperty(m, id, p); + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); uint index = id.asArrayIndex(); if (index < args->d()->argCount && !args->d()->fullyCreated) { diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index b5b421fa39..af1a2d1de0 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -175,7 +175,8 @@ static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObje // this isn't completely kosher. for instance: // Array.from.call(Object, []).constructor == Object // is expected by the tests, but naturally, we get Number. - ScopedValue argument(scope, useLen ? QV4::Encode(len) : Value::undefinedValue()); + ScopedValue argument(scope, useLen ? Value::fromReturnedValue(QV4::Encode(len)) + : Value::undefinedValue()); a = ctor->callAsConstructor(argument, useLen ? 1 : 0); } else { a = scope.engine->newArrayObject(len); @@ -219,7 +220,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V // Item iteration supported, so let's go ahead and try use that. ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0)); CHECK_EXCEPTION(); - ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true)); + ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true)); CHECK_EXCEPTION(); // symbol_iterator threw; whoops. if (!iterator) { return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. @@ -236,11 +237,11 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V if (k > (static_cast<qint64>(1) << 53) - 1) { ScopedValue falsey(scope, Encode(false)); ScopedValue error(scope, scope.engine->throwTypeError()); - return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + return Runtime::IteratorClose::call(scope.engine, iterator, falsey); } // Retrieve the next value. If the iteration ends, we're done here. - done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue)); + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue)); CHECK_EXCEPTION(); if (done->toBoolean()) { if (ArrayObject *ao = a->as<ArrayObject>()) { @@ -257,7 +258,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V mapArguments[1] = Value::fromDouble(k); mappedValue = mapfn->call(thisArg, mapArguments, 2); if (scope.engine->hasException) - return Runtime::method_iteratorClose(scope.engine, iterator, Value::fromBoolean(false)); + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); } else { mappedValue = *nextValue; } @@ -271,7 +272,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V if (scope.engine->hasException) { ScopedValue falsey(scope, Encode(false)); - return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + return Runtime::IteratorClose::call(scope.engine, iterator, falsey); } k++; @@ -387,7 +388,7 @@ ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, con v = instance->get(k); if (v->isNullOrUndefined()) continue; - v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); s = v->toString(scope.engine); if (scope.hasException()) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 3e5f51c302..a9b4ecb607 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -103,7 +103,7 @@ ReturnedValue BooleanPrototype::method_toString(const FunctionObject *b, const V if (exception) return v4->throwTypeError(); - return Encode(result ? v4->id_true() : v4->id_false()); + return (result ? v4->id_true() : v4->id_false())->asReturnedValue(); } ReturnedValue BooleanPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4compilationunitmapper.cpp index 65c3e4d65a..74f34a284d 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 <private/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/qv4serialize_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h index c8700c3ca5..80f914c141 100644 --- a/src/qml/jsruntime/qv4serialize_p.h +++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QV4SERIALIZE_P_H -#define QV4SERIALIZE_P_H +#ifndef QV4COMPILATIONUNITMAPPER_H +#define QV4COMPILATIONUNITMAPPER_H // // W A R N I N G @@ -51,26 +51,35 @@ // We mean it. // -#include <QtCore/qbytearray.h> -#include <private/qv4value_p.h> +#include <private/qv4global_p.h> +#include <QFile> QT_BEGIN_NAMESPACE namespace QV4 { -class Serialize { +namespace CompiledData { +struct Unit; +} + +class CompilationUnitMapper +{ public: + CompilationUnitMapper(); + ~CompilationUnitMapper(); - static QByteArray serialize(const Value &, ExecutionEngine *); - static ReturnedValue deserialize(const QByteArray &, ExecutionEngine *); + CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); + void close(); private: - static void serialize(QByteArray &, const Value &, ExecutionEngine *); - static ReturnedValue deserialize(const char *&, ExecutionEngine *); +#if defined(Q_OS_UNIX) + size_t length; +#endif + void *dataPtr; }; } QT_END_NAMESPACE -#endif // QV8WORKER_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..a9ab2f5ccb --- /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 "qv4executablecompilationunit_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 (!ExecutableCompilationUnit::verifyHeader(&header, 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..9e8babc5e6 --- /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 "qv4executablecompilationunit_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 (!ExecutableCompilationUnit::verifyHeader(&header, 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/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 94b1a9fb73..018571e325 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -60,7 +60,7 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b { Function *function = frame->v4Function; - Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex); + Heap::InternalClass *ic = function->executableCompilationUnit()->runtimeBlocks.at(blockIndex); uint nLocals = ic->size; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; @@ -71,27 +71,28 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + c->function.set(v4, static_cast<Heap::FunctionObject *>( + Value::fromStaticValue(frame->jsFrame->function).m())); c->locals.size = nLocals; c->locals.alloc = nLocals; - c->setupLocalTemporalDeadZone(function->compilationUnit->unitData()->blockAt(blockIndex)); + c->setupLocalTemporalDeadZone(function->executableCompilationUnit()->unitData()->blockAt(blockIndex)); return c; } -Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context) +Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine, + Heap::CallContext *callContext) { - uint nLocals = context->locals.alloc; + uint nLocals = callContext->locals.alloc; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; - ExecutionEngine *v4 = context->internalClass->engine; - Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass); - memcpy(c, context, requiredMemory); + Heap::CallContext *c = engine->memoryManager->allocManaged<CallContext>( + requiredMemory, callContext->internalClass); + memcpy(c, callContext, requiredMemory); return c; - } Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) @@ -108,7 +109,8 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) c->init(); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + c->function.set(v4, static_cast<Heap::FunctionObject *>( + Value::fromStaticValue(frame->jsFrame->function).m())); const CompiledData::Function *compiledFunction = function->compiledFunction; uint nLocals = compiledFunction->nLocals; @@ -128,7 +130,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) return c; } -Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) +Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) const { Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext); c->outer.set(engine(), d()); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 5cd2f9ddf0..75fa2d08e6 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -150,9 +150,10 @@ struct Q_QML_EXPORT ExecutionContext : public Managed V4_INTERNALCLASS(ExecutionContext) static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex); - static Heap::CallContext *cloneBlockContext(Heap::CallContext *context); + static Heap::CallContext *cloneBlockContext(ExecutionEngine *engine, + Heap::CallContext *callContext); static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); - Heap::ExecutionContext *newWithContext(Heap::Object *with); + Heap::ExecutionContext *newWithContext(Heap::Object *with) const; static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName); void createMutableBinding(String *name, bool deletable); diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index e313ebe300..cc89947cec 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -52,11 +52,9 @@ #include <time.h> -#include <private/qqmljsengine_p.h> - #include <wtf/MathExtras.h> -#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) +#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) && !defined(Q_OS_ANDROID) /* See QTBUG-56899. Although we don't (yet) have a proper way to reset the system zone, the code below, that uses QTimeZone::systemTimeZone(), works @@ -618,8 +616,7 @@ static inline double ParseString(const QString &s, double localTZA) QStringLiteral("d MMMM, yyyy hh:mm:ss"), }; - for (uint i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { - const QString &format(formats[i]); + for (const QString &format : formats) { dt = format.indexOf(QLatin1String("hh:mm")) < 0 ? QDateTime(QDate::fromString(s, format), QTime(0, 0, 0), Qt::UTC) @@ -746,14 +743,16 @@ void Heap::DateObject::init(const QTime &time) * time from it, which shall (via toQDateTime(), below) discard the date * part. We need a date for which time-zone data is likely to be sane (so * MakeDay(0, 0, 0) was a bad choice; 2 BC, December 31st is before - * time-zones were standardized), with no transition nearby in date. We - * ignore DST transitions before 1970, but even then zone transitions did - * happen. Some do happen at new year, others on DST transitions in spring - * and autumn; so pick the three hundredth anniversary of the birth of - * Giovanni Domenico Cassini (1625-06-08), whose work first let us - * synchronize clocks tolerably accurately at distant locations. + * time-zones were standardized), with no transition nearby in date. + * QDateTime ignores DST transitions before 1970, but even then zone + * transitions did happen; and DaylightSavingTA() will include DST, at odds + * with QDateTime. So pick a date since 1970 and prefer one when no zone + * was in DST. One such interval (according to the Olson database, at + * least) was 1971 March 15th to April 17th. Since converting a time to a + * date-time without specifying a date is foolish, let's use April Fools' + * day. */ - static const double d = MakeDay(1925, 5, 8); + static const double d = MakeDay(1971, 3, 1); double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA)); } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 5b9934282c..a87eb92caf 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -96,7 +96,7 @@ struct DateObject: Object { double date() const { return d()->date; } void setDate(double date) { d()->date = date; } - QDateTime toQDateTime() const; + Q_QML_PRIVATE_EXPORT QDateTime toQDateTime() const; }; template<> diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 9b41bb6e7a..52263105fa 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -52,6 +52,7 @@ // #include "qv4global_p.h" +#include <private/qv4staticvalue_p.h> #include <QtCore/qobject.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 3002945945..3d3f452073 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -38,21 +38,18 @@ ****************************************************************************/ #include <qv4engine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> #include <private/qv4compileddata_p.h> -#include <private/qv4compiler_p.h> -#include <private/qv4compilercontext_p.h> #include <private/qv4codegen_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <QtCore/QTextStream> #include <QDateTime> #include <QDir> #include <QFileInfo> #include <QLoggingCategory> - -#ifndef V4_BOOTSTRAP +#if QT_CONFIG(regularexpression) +#include <QRegularExpression> +#endif #include <qv4qmlcontext_p.h> #include <qv4value_p.h> @@ -104,7 +101,6 @@ #include "qv4dataview_p.h" #include "qv4promiseobject_p.h" #include "qv4typedarray_p.h" -#include <private/qv8engine_p.h> #include <private/qjsvalue_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmlvaluetypewrapper_p.h> @@ -112,9 +108,15 @@ #include <private/qqmllistwrapper_p.h> #include <private/qqmllist_p.h> #include <private/qqmltypeloader_p.h> +#include <private/qqmlbuiltinfunctions_p.h> #if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> #endif +#if QT_CONFIG(qml_xml_http_request) +#include <private/qv4domerrors_p.h> +#include <private/qqmlxmlhttprequest_p.h> +#endif +#include <private/qv4sqlerrors_p.h> #include <qqmlfile.h> #if USE(PTHREADS) @@ -131,16 +133,12 @@ #include <valgrind/memcheck.h> #endif -#endif // #ifndef V4_BOOTSTRAP +Q_DECLARE_METATYPE(QList<int>) QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all") - using namespace QV4; -#ifndef V4_BOOTSTRAP - static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int) @@ -150,6 +148,43 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const qint32 ExecutionEngine::maxCallDepth = -1; +template <typename ReturnType> +ReturnType convertJSValueToVariantType(const QJSValue &value) +{ + return value.toVariant().value<ReturnType>(); +} + +static void saveJSValue(QDataStream &stream, const void *data) +{ + const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data); + quint32 isNullOrUndefined = 0; + if (jsv->isNull()) + isNullOrUndefined |= 0x1; + if (jsv->isUndefined()) + isNullOrUndefined |= 0x2; + stream << isNullOrUndefined; + if (!isNullOrUndefined) + reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream); +} + +static void restoreJSValue(QDataStream &stream, void *data) +{ + QJSValue *jsv = reinterpret_cast<QJSValue*>(data); + + quint32 isNullOrUndefined; + stream >> isNullOrUndefined; + + if (isNullOrUndefined & 0x1) { + *jsv = QJSValue(QJSValue::NullValue); + } else if (isNullOrUndefined & 0x2) { + *jsv = QJSValue(); + } else { + QVariant v; + v.load(stream); + QJSValuePrivate::setVariant(jsv, v); + } +} + ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) @@ -157,19 +192,31 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) , jsStack(new WTF::PageAllocation) , gcStack(new WTF::PageAllocation) , globalCode(nullptr) - , v8Engine(nullptr) , publicEngine(jsEngine) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(nullptr) , m_multiplyWrappedQObjects(nullptr) -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) , m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory()) #endif +#if QT_CONFIG(qml_xml_http_request) + , m_xmlHttpRequestData(nullptr) +#endif + , m_qmlEngine(nullptr) { + bool ok = false; + const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok); + if (ok && envMaxJSStackSize > 0) + m_maxJSStackSize = envMaxJSStackSize; + + const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok); + if (ok && envMaxGCStackSize > 0) + m_maxGCStackSize = envMaxGCStackSize; + memoryManager = new QV4::MemoryManager(this); if (maxCallDepth == -1) { - bool ok = false; + ok = false; maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); if (!ok || maxCallDepth <= 0) { #if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !QT_HAS_FEATURE(address_sanitizer) @@ -183,24 +230,24 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) Q_ASSERT(maxCallDepth > 0); // reserve space for the JS stack - // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues + // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues // allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(JSStackLimit + 256*1024, WTF::OSAllocator::JSVMStackPages, + *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, JSStackLimit + 256*1024); + VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024); #endif jsStackTop = jsStackBase; - *gcStack = WTF::PageAllocation::allocate(GCStackLimit, WTF::OSAllocator::JSVMStackPages, + *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); { - bool ok = false; + ok = false; jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok); if (!ok) jitCallCountThreshold = 3; @@ -218,7 +265,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsSymbols = jsAlloca(NJSSymbols); // set up stack limits - jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); + jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value); identifierTable = new IdentifierTable(this); @@ -643,7 +690,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError)); t->defineReadonlyProperty(id_length(), Value::fromInt32(0)); - t->setInternalClass(t->internalClass()->frozen()); + t->setInternalClass(t->internalClass()->cryopreserved()); jsObjects[ThrowerObject] = t; ScopedProperty pd(scope); @@ -651,18 +698,27 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) pd->set = thrower(); functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); + + qMetaTypeId<QJSValue>(); + qMetaTypeId<QList<int> >(); + + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>()) + QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>()) + QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>()) + QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); + QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue); + + QV4::QObjectWrapper::initializeBindings(this); + + m_delayedCallQueue.init(this); } ExecutionEngine::~ExecutionEngine() { - if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) { - for (auto cu : compilationUnits) { - for (auto f : qAsConst(cu->runtimeFunctions)) - qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString(); - } - } - modules.clear(); + qDeleteAll(m_extensionData); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; delete identifierTable; @@ -679,6 +735,11 @@ ExecutionEngine::~ExecutionEngine() delete jsStack; gcStack->deallocate(); delete gcStack; + +#if QT_CONFIG(qml_xml_http_request) + qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); + m_xmlHttpRequestData = nullptr; +#endif } ExecutionContext *ExecutionEngine::currentContext() const @@ -860,6 +921,13 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) return memoryManager->allocate<RegExpObject>(re); } +#if QT_CONFIG(regularexpression) +Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re) +{ + return memoryManager->allocate<RegExpObject>(re); +} +#endif + Heap::Object *ExecutionEngine::newErrorObject(const Value &value) { return ErrorObject::create<ErrorObject>(this, value, errorCtor()); @@ -1072,6 +1140,12 @@ extern "C" Q_QML_EXPORT char *qt_v4StackTrace(void *executionContext) return v4StackTrace(reinterpret_cast<const ExecutionContext *>(executionContext)); } +extern "C" Q_QML_EXPORT char *qt_v4StackTraceForEngine(void *executionEngine) +{ + auto engine = (reinterpret_cast<const ExecutionEngine *>(executionEngine)); + return v4StackTrace(engine->currentContext()); +} + QUrl ExecutionEngine::resolvedUrl(const QString &file) { QUrl src(file); @@ -1269,6 +1343,7 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &va const QByteArray &targetType, void **result); static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst); +static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst); static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap); static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value) { @@ -1305,7 +1380,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) { - return qVariantFromValue<QObject *>(wrapper->object()); + return QVariant::fromValue<QObject *>(wrapper->object()); } else if (object->as<QV4::QQmlContextWrapper>()) { return QVariant(); } else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) { @@ -1336,7 +1411,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int } } - return qVariantFromValue<QList<QObject*> >(list); + return QVariant::fromValue<QList<QObject*> >(list); } else if (typeHint == QMetaType::QJsonArray) { return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } @@ -1379,8 +1454,13 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int QV4::ScopedObject o(scope, value); Q_ASSERT(o); - if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) + if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) { +#if QT_CONFIG(regularexpression) + if (typeHint != QMetaType::QRegExp) + return re->toQRegularExpression(); +#endif return re->toQRegExp(); + } if (createJSValueForObjects) return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue())); @@ -1442,35 +1522,6 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V return result; } -static QV4::ReturnedValue arrayFromVariantList(QV4::ExecutionEngine *e, const QVariantList &list) -{ - QV4::Scope scope(e); - QV4::ScopedArrayObject a(scope, e->newArrayObject()); - int len = list.count(); - a->arrayReserve(len); - QV4::ScopedValue v(scope); - for (int ii = 0; ii < len; ++ii) - a->arrayPut(ii, (v = scope.engine->fromVariant(list.at(ii)))); - - a->setArrayLengthUnchecked(len); - return a.asReturnedValue(); -} - -static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QVariantMap &map) -{ - QV4::Scope scope(e); - QV4::ScopedObject o(scope, e->newObject()); - QV4::ScopedString s(scope); - QV4::ScopedValue v(scope); - for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) { - s = e->newString(iter.key()); - o->put(s, (v = e->fromVariant(iter.value()))); - } - return o.asReturnedValue(); -} - -Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); - QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) { int type = variant.userType(); @@ -1520,6 +1571,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr))); case QMetaType::QRegExp: return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: + return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr))); +#endif case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); #if QT_CONFIG(qml_sequence_object) @@ -1534,9 +1589,9 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) } #endif case QMetaType::QVariantList: - return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); + return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr)); case QMetaType::QVariantMap: - return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr)); + return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr)); case QMetaType::QJsonValue: return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(ptr)); case QMetaType::QJsonObject: @@ -1597,6 +1652,11 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return retn->asReturnedValue(); #endif + if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) { + QSequentialIterable lst = variant.value<QSequentialIterable>(); + return sequentialIterableToJS(this, lst); + } + if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); } @@ -1661,9 +1721,8 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian s = v4->newIdentifier(it.key()); key = s->propertyKey(); v = variantToJS(v4, it.value()); - uint idx = key->asArrayIndex(); - if (idx < UINT_MAX) - o->arraySet(idx, v); + if (key->isArrayIndex()) + o->arraySet(key->asArrayIndex(), v); else o->insertMember(s, v); } @@ -1676,99 +1735,23 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) { Q_ASSERT(data != nullptr); - // check if it's one of the types we know - switch (QMetaType::Type(type)) { - case QMetaType::UnknownType: - case QMetaType::Void: - return QV4::Encode::undefined(); - case QMetaType::Nullptr: - case QMetaType::VoidStar: - return QV4::Encode::null(); - case QMetaType::Bool: - return QV4::Encode(*reinterpret_cast<const bool*>(data)); - case QMetaType::Int: - return QV4::Encode(*reinterpret_cast<const int*>(data)); - case QMetaType::UInt: - return QV4::Encode(*reinterpret_cast<const uint*>(data)); - case QMetaType::LongLong: - return QV4::Encode(double(*reinterpret_cast<const qlonglong*>(data))); - case QMetaType::ULongLong: -#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 -#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") - return QV4::Encode(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); -#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) - return QV4::Encode(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); -#else - return QV4::Encode(double(*reinterpret_cast<const qulonglong*>(data))); -#endif - case QMetaType::Double: - return QV4::Encode(*reinterpret_cast<const double*>(data)); - case QMetaType::QString: - return newString(*reinterpret_cast<const QString*>(data))->asReturnedValue(); - case QMetaType::QByteArray: - return newArrayBuffer(*reinterpret_cast<const QByteArray*>(data))->asReturnedValue(); - case QMetaType::Float: - return QV4::Encode(*reinterpret_cast<const float*>(data)); - case QMetaType::Short: - return QV4::Encode((int)*reinterpret_cast<const short*>(data)); - case QMetaType::UShort: - return QV4::Encode((int)*reinterpret_cast<const unsigned short*>(data)); - case QMetaType::Char: - return QV4::Encode((int)*reinterpret_cast<const char*>(data)); - case QMetaType::UChar: - return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(data)); - case QMetaType::QChar: - return QV4::Encode((int)(*reinterpret_cast<const QChar*>(data)).unicode()); - case QMetaType::QStringList: - return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(data))); - case QMetaType::QVariantList: - return variantListToJS(this, *reinterpret_cast<const QVariantList *>(data)); - case QMetaType::QVariantMap: - return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(data)); - case QMetaType::QDateTime: - return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(data))); - case QMetaType::QDate: - return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(data)))); - case QMetaType::QRegExp: - return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(data))); - case QMetaType::QObjectStar: - return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data)); - case QMetaType::QVariant: + QVariant variant(type, data); + if (QMetaType::Type(variant.type()) == QMetaType::QVariant) { + // unwrap it: this is tested in QJSEngine, and makes the most sense for + // end-user code too. return variantToJS(this, *reinterpret_cast<const QVariant*>(data)); - case QMetaType::QJsonValue: - return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(data)); - case QMetaType::QJsonObject: - return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(data)); - case QMetaType::QJsonArray: - return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(data)); - default: - if (type == qMetaTypeId<QJSValue>()) { - return QJSValuePrivate::convertedToValue(this, *reinterpret_cast<const QJSValue*>(data)); - } else { - QByteArray typeName = QMetaType::typeName(type); - if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) { - return QV4::Encode::null(); - } - QMetaType mt(type); - if (auto metaObject = mt.metaObject()) { - auto flags = mt.flags(); - if (flags & QMetaType::IsGadget) { - return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), metaObject, type); - } else if (flags & QMetaType::PointerToQObject) { - return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data)); - } - } - if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) { - auto v = QVariant(type, data); - QSequentialIterable lst = v.value<QSequentialIterable>(); - return sequentialIterableToJS(this, lst); - } - // Fall back to wrapping in a QVariant. - return QV4::Encode(newVariantObject(QVariant(type, data))); - } } - Q_UNREACHABLE(); - return 0; + return fromVariant(variant); +} + +int ExecutionEngine::maxJSStackSize() const +{ + return m_maxJSStackSize; +} + +int ExecutionEngine::maxGCStackSize() const +{ + return m_maxGCStackSize; } ReturnedValue ExecutionEngine::global() @@ -1776,13 +1759,12 @@ ReturnedValue ExecutionEngine::global() return globalObject->asReturnedValue(); } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(const QUrl &url) { QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url, &cacheError)) { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> jsUnit; - jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString())); - return jsUnit; + return ExecutableCompilationUnit::create( + QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString())); } QFile f(QQmlFile::urlToLocalFileOrQrc(url)); @@ -1800,69 +1782,26 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(con } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule( + const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp) { QList<QQmlJS::DiagnosticMessage> diagnostics; - auto unit = compileModule(/*debugMode*/debugger() != nullptr, url.toString(), sourceCode, sourceTimeStamp, &diagnostics); + auto unit = Compiler::Codegen::compileModule(/*debugMode*/debugger() != nullptr, url.toString(), + sourceCode, sourceTimeStamp, &diagnostics); for (const QQmlJS::DiagnosticMessage &m : diagnostics) { if (m.isError()) { - throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn); + throwSyntaxError(m.message, url.toString(), m.line, m.column); return nullptr; } else { - qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn + qWarning() << url << ':' << m.line << ':' << m.column << ": warning: " << m.message; } } - return unit; -} - -#endif // ifndef V4_BOOTSTRAP - -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(bool debugMode, const QString &url, const QString &sourceCode, - const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics) -{ - QQmlJS::Engine ee; - QQmlJS::Lexer lexer(&ee); - lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false); - QQmlJS::Parser parser(&ee); - - const bool parsed = parser.parseModule(); - - if (diagnostics) - *diagnostics = parser.diagnosticMessages(); - - if (!parsed) - return nullptr; - - QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode()); - if (!moduleNode) { - // if parsing was successful, and we have no module, then - // the file was empty. - if (diagnostics) - diagnostics->clear(); - return nullptr; - } - - using namespace QV4::Compiler; - Compiler::Module compilerModule(debugMode); - compilerModule.unitFlags |= CompiledData::Unit::IsESModule; - compilerModule.sourceTimeStamp = sourceTimeStamp; - JSUnitGenerator jsGenerator(&compilerModule); - Codegen cg(&jsGenerator, /*strictMode*/true); - cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule); - auto errors = cg.errors(); - if (diagnostics) - *diagnostics << errors; - - if (!errors.isEmpty()) - return nullptr; - return cg.generateCompilationUnit(); + return ExecutableCompilationUnit::create(std::move(unit)); } -#ifndef V4_BOOTSTRAP - -void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit) +void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit) { // Injection can happen from the QML type loader thread for example, but instantiation and // evaluation must be limited to the ExecutionEngine's thread. @@ -1870,7 +1809,7 @@ void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::Compilatio modules.insert(moduleUnit->finalUrl(), moduleUnit); } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer) const +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer) const { QUrl url = QQmlTypeLoader::normalize(_url); if (referrer) @@ -1883,7 +1822,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(cons return *existingModule; } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer) { QUrl url = QQmlTypeLoader::normalize(_url); if (referrer) @@ -1905,6 +1844,131 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const return newModule; } +void ExecutionEngine::initQmlGlobalObject() +{ + initializeGlobal(); + freezeObject(*globalObject); +} + +void ExecutionEngine::initializeGlobal() +{ + QV4::Scope scope(this); + QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); + + QV4::ScopedObject qt(scope, memoryManager->allocate<QV4::QtObject>(qmlEngine())); + globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); + +#if QT_CONFIG(qml_locale) + QQmlLocale::registerStringLocaleCompare(this); + QQmlDateExtension::registerExtension(this); + QQmlNumberExtension::registerExtension(this); +#endif + +#if QT_CONFIG(qml_xml_http_request) + qt_add_domexceptions(this); + m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); +#endif + + qt_add_sqlexceptions(this); + + { + for (uint i = 0; i < globalObject->internalClass()->size; ++i) { + if (globalObject->internalClass()->nameMap.at(i).isString()) { + QV4::PropertyKey id = globalObject->internalClass()->nameMap.at(i); + m_illegalNames.insert(id.toQString()); + } + } + } +} + +const QSet<QString> &ExecutionEngine::illegalNames() const +{ + return m_illegalNames; +} + +void ExecutionEngine::setQmlEngine(QQmlEngine *engine) +{ + m_qmlEngine = engine; + initQmlGlobalObject(); +} + +static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) +{ + if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen) + return; + + QV4::Scope scope(v4); + + bool instanceOfObject = false; + QV4::ScopedObject p(scope, object->getPrototypeOf()); + while (p) { + if (p->d() == v4->objectPrototype()->d()) { + instanceOfObject = true; + break; + } + p = p->getPrototypeOf(); + } + if (!instanceOfObject) + return; + + Heap::InternalClass *frozen = object->internalClass()->frozen(); + object->setInternalClass(frozen); // Immediately assign frozen to prevent it from getting GC'd + + QV4::ScopedObject o(scope); + for (uint i = 0; i < frozen->size; ++i) { + if (!frozen->nameMap.at(i).isStringOrSymbol()) + continue; + o = *object->propertyData(i); + if (o) + freeze_recursive(v4, o); + } +} + +void ExecutionEngine::freezeObject(const QV4::Value &value) +{ + QV4::Scope scope(this); + QV4::ScopedObject o(scope, value); + freeze_recursive(this, o); +} + +void ExecutionEngine::startTimer(const QString &timerName) +{ + if (!m_time.isValid()) + m_time.start(); + m_startedTimers[timerName] = m_time.elapsed(); +} + +qint64 ExecutionEngine::stopTimer(const QString &timerName, bool *wasRunning) +{ + if (!m_startedTimers.contains(timerName)) { + *wasRunning = false; + return 0; + } + *wasRunning = true; + qint64 startedAt = m_startedTimers.take(timerName); + return m_time.elapsed() - startedAt; +} + +int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint16 column) +{ + const QString key = file + QString::number(line) + QString::number(column); + int number = m_consoleCount.value(key, 0); + number++; + m_consoleCount.insert(key, number); + return number; +} + +void ExecutionEngine::setExtensionData(int index, Deletable *data) +{ + if (m_extensionData.count() <= index) + m_extensionData.resize(index + 1); + + if (m_extensionData.at(index)) + delete m_extensionData.at(index); + + m_extensionData[index] = data; +} + // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. @@ -1980,6 +2044,13 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) *reinterpret_cast<QRegExp *>(data) = r->toQRegExp(); return true; } break; +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: + if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) { + *reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression(); + return true; + } break; +#endif case QMetaType::QObjectStar: { const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>(); if (qobjectWrapper || value->isNull()) { @@ -2144,6 +2215,23 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &v return wrapper->object(); } -#endif // ifndef V4_BOOTSTRAP +struct QV4EngineRegistrationData +{ + QV4EngineRegistrationData() : extensionCount(0) {} + + QMutex mutex; + int extensionCount; +}; +Q_GLOBAL_STATIC(QV4EngineRegistrationData, registrationData); + +QMutex *ExecutionEngine::registrationMutex() +{ + return ®istrationData()->mutex; +} + +int ExecutionEngine::registerExtension() +{ + return registrationData()->extensionCount++; +} QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 86367c0ece..d233347060 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -53,25 +53,82 @@ #include "qv4global_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" +#include "qv4stackframe_p.h" #include <private/qintrusivelist_p.h> #include "qv4enginebase_p.h" #include <private/qqmlrefcount_p.h> -#include <private/qqmljsengine_p.h> +#include <private/qqmldelayedcallqueue_p.h> +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qmutex.h> -#ifndef V4_BOOTSTRAP -# include "qv4function_p.h" -# include <private/qv8engine_p.h> -# include <private/qv4compileddata_p.h> -#endif +#include "qv4function_p.h" +#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> namespace WTF { class BumpPointerAllocator; class PageAllocation; } +#define V4_DEFINE_EXTENSION(dataclass, datafunction) \ + static inline dataclass *datafunction(QV4::ExecutionEngine *engine) \ + { \ + static int extensionId = -1; \ + if (extensionId == -1) { \ + QV4::ExecutionEngine::registrationMutex()->lock(); \ + if (extensionId == -1) \ + extensionId = QV4::ExecutionEngine::registerExtension(); \ + QV4::ExecutionEngine::registrationMutex()->unlock(); \ + } \ + dataclass *rv = (dataclass *)engine->extensionData(extensionId); \ + if (!rv) { \ + rv = new dataclass(engine); \ + engine->setExtensionData(extensionId, rv); \ + } \ + return rv; \ + } \ + + QT_BEGIN_NAMESPACE -class QV8Engine; +namespace QV4 { struct QObjectMethod; } + +// Used to allow a QObject method take and return raw V4 handles without having to expose +// 48 in the public API. +// Use like this: +// class MyClass : public QObject { +// Q_OBJECT +// ... +// Q_INVOKABLE void myMethod(QQmlV4Function*); +// }; +// The QQmlV8Function - and consequently the arguments and return value - only remains +// valid during the call. If the return value isn't set within myMethod(), the will return +// undefined. + +class QQmlV4Function +{ +public: + int length() const { return callData->argc(); } + QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } + void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; } + QV4::ExecutionEngine *v4engine() const { return e; } +private: + friend struct QV4::QObjectMethod; + QQmlV4Function(); + QQmlV4Function(const QQmlV4Function &); + QQmlV4Function &operator=(const QQmlV4Function &); + + QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e) + : callData(callData), retVal(retVal), e(e) + { + callData->thisObject = QV4::Encode::undefined(); + } + + QV4::CallData *callData; + QV4::Value *retVal; + QV4::ExecutionEngine *e; +}; + class QQmlError; class QJSEngine; class QQmlEngine; @@ -112,10 +169,6 @@ public: WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. - enum { - JSStackLimit = 4*1024*1024, - GCStackLimit = 2*1024*1024 - }; WTF::PageAllocation *jsStack; WTF::PageAllocation *gcStack; @@ -128,14 +181,8 @@ public: Function *globalCode; -#ifdef V4_BOOTSTRAP - QJSEngine *jsEngine() const; - QQmlEngine *qmlEngine() const; -#else // !V4_BOOTSTRAP QJSEngine *jsEngine() const { return publicEngine; } - QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; } -#endif // V4_BOOTSTRAP - QV8Engine *v8Engine; + QQmlEngine *qmlEngine() const { return m_qmlEngine; } QJSEngine *publicEngine; enum JSObjects { @@ -438,9 +485,7 @@ public: Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } -#ifndef V4_BOOTSTRAP - QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits; -#endif + QIntrusiveList<ExecutableCompilationUnit, &ExecutableCompilationUnit::nextCompilationUnit> compilationUnits; quint32 m_engineId; @@ -464,7 +509,7 @@ public: // but any time a QObject is wrapped a second time in another engine, we have to do // bookkeeping. MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) const bool m_canAllocateExecutableMemory; #endif @@ -520,6 +565,9 @@ public: Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re); Heap::RegExpObject *newRegExpObject(const QRegExp &re); +#if QT_CONFIG(regularexpression) + Heap::RegExpObject *newRegExpObject(const QRegularExpression &re); +#endif Heap::Object *newErrorObject(const Value &value); Heap::Object *newErrorObject(const QString &message); @@ -588,11 +636,14 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); + int maxJSStackSize() const; + int maxGCStackSize() const; + bool checkStackLimits(); bool canJIT(Function *f = nullptr) { -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) if (!m_canAllocateExecutableMemory) return false; if (f) @@ -605,42 +656,86 @@ public: } QV4::ReturnedValue global(); + void initQmlGlobalObject(); + void initializeGlobal(); + + void freezeObject(const QV4::Value &value); + + // Return the list of illegal id names (the names of the properties on the global object) + const QSet<QString> &illegalNames() const; + +#if QT_CONFIG(qml_xml_http_request) + void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } +#endif + + void setQmlEngine(QQmlEngine *engine); + + QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } + + // used for console.time(), console.timeEnd() + void startTimer(const QString &timerName); + qint64 stopTimer(const QString &timerName, bool *wasRunning); + + // used for console.count() + int consoleCountHelper(const QString &file, quint16 line, quint16 column); + + struct Deletable { + virtual ~Deletable() {} + }; + + static QMutex *registrationMutex(); + static int registerExtension(); + + void setExtensionData(int, Deletable *); + Deletable *extensionData(int index) const + { + if (index < m_extensionData.count()) + return m_extensionData[index]; + else + return nullptr; + } double localTZA = 0.0; // local timezone, initialized at startup - static QQmlRefPointer<CompiledData::CompilationUnit> compileModule(bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics); -#ifndef V4_BOOTSTRAP - QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url); - QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp); + QQmlRefPointer<ExecutableCompilationUnit> compileModule(const QUrl &url); + QQmlRefPointer<ExecutableCompilationUnit> compileModule( + const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp); mutable QMutex moduleMutex; - QHash<QUrl, QQmlRefPointer<CompiledData::CompilationUnit>> modules; - void injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit); - QQmlRefPointer<CompiledData::CompilationUnit> moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr) const; - QQmlRefPointer<CompiledData::CompilationUnit> loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr); -#endif + QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules; + void injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit); + QQmlRefPointer<ExecutableCompilationUnit> moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const; + QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; QScopedPointer<QV4::Profiling::Profiler> m_profiler; #endif + QSet<QString> m_illegalNames; int jitCallCountThreshold; // used by generated Promise objects to handle 'then' events QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler; -}; -// This is a trick to tell the code generators that functions taking a NoThrowContext won't -// throw exceptions and therefore don't need a check after the call. -#ifndef V4_BOOTSTRAP -struct NoThrowEngine : public ExecutionEngine -{ -}; -#else -struct NoThrowEngine; +#if QT_CONFIG(qml_xml_http_request) + void *m_xmlHttpRequestData; #endif + QQmlEngine *m_qmlEngine; + + QQmlDelayedCallQueue m_delayedCallQueue; + + QElapsedTimer m_time; + QHash<QString, qint64> m_startedTimers; + + QHash<QString, quint32> m_consoleCount; + + QVector<Deletable *> m_extensionData; + + int m_maxJSStackSize = 4 * 1024 * 1024; + int m_maxGCStackSize = 2 * 1024 * 1024; +}; #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index b5cfea8863..788897bdad 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -69,9 +69,20 @@ struct Q_QML_EXPORT EngineBase { CppStackFrame *currentStackFrame = nullptr; Value *jsStackTop = nullptr; + + // The JIT expects hasException and isInterrupted to be in the same 32bit word in memory. quint8 hasException = false; - quint8 writeBarrierActive = false; + // isInterrupted is expected to be set from a different thread +#if defined(Q_ATOMIC_INT8_IS_SUPPORTED) + QAtomicInteger<quint8> isInterrupted = false; quint16 unused = 0; +#elif defined(Q_ATOMIC_INT16_IS_SUPPORTED) + quint8 unused = 0; + QAtomicInteger<quint16> isInterrupted = false; +#else +# error V4 needs either 8bit or 16bit atomics. +#endif + quint8 isExecutingInRegExpJIT = false; quint8 padding[3]; MemoryManager *memoryManager = nullptr; @@ -136,6 +147,7 @@ Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + 8); Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, isInterrupted) + sizeof(EngineBase::isInterrupted) <= offsetof(EngineBase, hasException) + 4); } diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index c6d6c77d11..525d3458f4 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -47,7 +47,7 @@ #include "qv4string_p.h" #include <private/qv4mm_p.h> -#include <qv4codegen_p.h> +#include <private/qv4codegen_p.h> #ifndef Q_OS_WIN # include <time.h> diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp index c836d121e3..7ee6f39aa2 100644 --- a/src/qml/jsruntime/qv4executableallocator.cpp +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -147,9 +147,7 @@ bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const } ExecutableAllocator::ExecutableAllocator() - : mutex(QMutex::NonRecursive) -{ -} + = default; ExecutableAllocator::~ExecutableAllocator() { diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index 013c6d7120..f98f2c7d33 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -130,7 +130,7 @@ public: private: QMultiMap<size_t, Allocation*> freeAllocations; QMap<quintptr, ChunkOfPages*> chunks; - mutable QMutex mutex; + mutable QRecursiveMutex mutex; }; } diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp new file mode 100644 index 0000000000..79e2ec2a5d --- /dev/null +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -0,0 +1,881 @@ +/**************************************************************************** +** +** 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/qv4objectproto_p.h> +#include <private/qqmlengine_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qqmlscriptdata_p.h> +#include <private/qv4module_p.h> +#include <private/qv4compilationunitmapper_p.h> +#include <private/qml_compile_hash_p.h> +#include <private/qqmltypewrapper_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> +#include <QtCore/qcryptographichash.h> + +#if defined(QML_COMPILE_HASH) +# ifdef Q_OS_LINUX +// Place on a separate section on Linux so it's easier to check from outside +// what the hash version is. +__attribute__((section(".qml_compile_hash"))) +# endif +const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; +static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, + "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); +#else +# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +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 + || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { + if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) + pc->release(); + } else if (l.getter == QQmlValueTypeWrapper::lookupGetter + || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { + if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) + pc->release(); + } + + if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty + || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) { + 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(); + + free(runtimeStrings); + runtimeStrings = nullptr; + delete [] runtimeRegularExpressions; + runtimeRegularExpressions = nullptr; + free(runtimeClasses); + runtimeClasses = nullptr; +} + +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 CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>( + [&unitUrl, errorString](const char *data, quint32 size) { + return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data, + size, 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), 'g', QLocale::FloatingPointShortest); + case Binding::Type_Invalid: + return QString(); +#if !QT_CONFIG(translation) + case Binding::Type_TranslationById: + case Binding::Type_Translation: + return stringAt( + 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); +} + +bool ExecutableCompilationUnit::verifyHeader( + const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString) +{ + if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2") + .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (unit->qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2") + .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + if (unit->sourceTimeStamp) { + // Files from the resource system do not have any time stamps, so fall back to the application + // executable. + if (!expectedSourceTimeStamp.isValid()) + expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); + + if (expectedSourceTimeStamp.isValid() + && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + +#if defined(QML_COMPILE_HASH) + if (qstrcmp(qml_compile_hash, unit->libraryVersionHash) != 0) { + *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); + return false; + } +#else +#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" +#endif + return true; +} + +} // 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..6eef3b12c3 --- /dev/null +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** 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/qv4identifier_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qintrusivelist_p.h> +#include <private/qqmlpropertycachevector_p.h> +#include <private/qqmltype_p.h> +#include <private/qqmlnullablevalue_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlScriptData; +class QQmlEnginePrivate; +namespace QV4 { + +// index is per-object binding index +typedef QVector<QQmlPropertyData*> BindingPropertyData; + +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<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(); + } + + static bool verifyHeader(const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, + QString *errorString); + +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/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 1bd4329fe8..aeb4835c40 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -73,24 +73,19 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg return result; } -Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) +Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function) { - quint16 traceSlotCount = 0; -#if QT_CONFIG(qml_tracing) - traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing() - ? 1 - : function->nTraceInfos; -#endif - quint8 *storage = new quint8[sizeof(Function) + traceSlotCount]; - return new(storage) Function(engine, unit, function); + return new Function(engine, unit, function); } void Function::destroy() { - delete[] reinterpret_cast<quint8 *>(this); + delete this; } -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) +Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function) : FunctionData(unit) , compiledFunction(function) , codeData(function->code()) @@ -105,19 +100,12 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, for (quint32 i = 0; i < compiledFunction->nLocals; ++i) ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); - const quint32_le *formalsIndices = compiledFunction->formalsTable(); + const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable(); for (quint32 i = 0; i < compiledFunction->nFormals; ++i) - ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable); internalClass = ic->d(); nFormals = compiledFunction->nFormals; - -#if QT_CONFIG(qml_tracing) - if (tracingEnabled()) { - for (uint i = 0; i < function->nTraceInfos; ++i) - *traceInfo(i) = 0; - } -#endif } Function::~Function() @@ -160,8 +148,11 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); - for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + for (quint32 i = 0; i < compiledFunction->nLocals; ++i) { + internalClass = internalClass->addMember( + engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), + Attr_NotConfigurable); + } Scope scope(engine); ScopedString arg(scope); @@ -188,22 +179,4 @@ QQmlSourceLocation Function::sourceLocation() const return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } -QString Function::traceInfoToString() -{ - QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':'); - if (!tracingEnabled()) - return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount); - if (compiledFunction->nTraceInfos == 0) - return info + QLatin1String(" none.\n"); - - info += QLatin1Char('\n'); - for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) { - auto bits = QString::number(*traceInfo(i), 2); - if (bits.size() < 8) - bits.prepend(QString(8 - bits.size(), '0')); - info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits); - } - return info; -} - QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index f8125a58f8..51960863c4 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -51,8 +51,9 @@ // #include "qv4global_p.h" -#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> #include <private/qv4context_p.h> +#include <private/qv4string_p.h> namespace JSC { class MacroAssemblerCodeRef; @@ -65,9 +66,13 @@ struct QQmlSourceLocation; namespace QV4 { struct Q_QML_EXPORT FunctionData { - CompiledData::CompilationUnit *compilationUnit; + CompiledData::CompilationUnitBase *compilationUnit; - FunctionData(CompiledData::CompilationUnit *compilationUnit) + // Intentionally require an ExecutableCompilationUnit but save only a pointer to + // CompilationUnitBase. This is so that we can take advantage of the standard layout + // of CompilationUnitBase in the JIT. Furthermore we can safely static_cast to + // ExecutableCompilationUnit where we need it. + FunctionData(ExecutableCompilationUnit *compilationUnit) : compilationUnit(compilationUnit) {} }; @@ -76,12 +81,24 @@ Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value); struct Q_QML_EXPORT Function : public FunctionData { private: - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function); ~Function(); public: const CompiledData::Function *compiledFunction; + QV4::ExecutableCompilationUnit *executableCompilationUnit() const + { + // This is safe: We require an ExecutableCompilationUnit in the ctor. + return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit); + } + + QV4::Heap::String *runtimeString(uint i) const + { + return compilationUnit->runtimeStrings[i]; + } + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); const char *codeData; @@ -96,20 +113,21 @@ public: int interpreterCallCount = 0; bool isEval = false; - static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + static Function *create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function); void destroy(); // used when dynamically assigning signal handlers (QQmlConnection) void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); inline Heap::String *name() const { - return compilationUnit->runtimeStrings[compiledFunction->nameIndex]; + return runtimeString(compiledFunction->nameIndex); } static QString prettyName(const Function *function, const void *address); - inline QString sourceFile() const { return compilationUnit->fileName(); } - inline QUrl finalUrl() const { return compilationUnit->finalUrl(); } + inline QString sourceFile() const { return executableCompilationUnit()->fileName(); } + inline QUrl finalUrl() const { return executableCompilationUnit()->finalUrl(); } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; } @@ -121,32 +139,7 @@ public: { if (compiledFunction->nestedFunctionIndex == std::numeric_limits<uint32_t>::max()) return nullptr; - return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; - } - - Q_NEVER_INLINE QString traceInfoToString(); - - quint8 *traceInfo(uint i) - { -#if QT_CONFIG(qml_tracing) - Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0)); - return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i; -#else - Q_UNUSED(i); - return nullptr; -#endif - } - - quint32 traceInfoCount() const - { return compiledFunction->nTraceInfos; } - - bool tracingEnabled() const - { -#if QT_CONFIG(qml_tracing) - return traceInfoCount() != CompiledData::Function::NoTracing(); -#else - return false; -#endif + return executableCompilationUnit()->runtimeFunctions[compiledFunction->nestedFunctionIndex]; } }; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 41a21ba379..6fb7946023 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -59,10 +59,10 @@ #include "private/qqmlbuiltinfunctions_p.h" #include <private/qv4jscall_p.h> #include <private/qv4vme_moth_p.h> +#include <private/qv4alloca_p.h> #include <QtCore/QDebug> #include <algorithm> -#include "qv4alloca_p.h" #include "qv4profiling_p.h" using namespace QV4; @@ -135,13 +135,13 @@ void Heap::FunctionObject::setFunction(Function *f) { if (f) { function = f; - function->compilationUnit->addref(); + function->executableCompilationUnit()->addref(); } } void Heap::FunctionObject::destroy() { if (function) - function->compilationUnit->release(); + function->executableCompilationUnit()->release(); Object::destroy(); } @@ -229,7 +229,7 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) } // 15.3.2 -QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) +QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) { QString arguments; QString body; @@ -273,14 +273,15 @@ QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngin if (engine->hasException) return nullptr; - return cg.generateCompilationUnit(); + return ExecutableCompilationUnit::create(cg.generateCompilationUnit()); } ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *engine = f->engine(); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function); + QQmlRefPointer<ExecutableCompilationUnit> compilationUnit + = parse(engine, argv, argc, Type_Function); if (engine->hasException) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index e03d49c74d..c99cad8e33 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -87,11 +87,11 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { } Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); - void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); - void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); - void init(QV4::ExecutionContext *scope, const QString &name); - void init(); - void destroy(); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); + Q_QML_PRIVATE_EXPORT void init(); + Q_QML_PRIVATE_EXPORT void destroy(); void setFunction(Function *f); @@ -244,7 +244,7 @@ protected: Type_Function, Type_Generator }; - static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); + static QQmlRefPointer<ExecutableCompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); }; struct FunctionPrototype: FunctionObject @@ -260,7 +260,7 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; -struct IndexedBuiltinFunction : FunctionObject +struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) }; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 14caa6953f..4eee6f4338 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -58,7 +58,7 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje { ExecutionEngine *engine = f->engine(); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator); + QQmlRefPointer<ExecutableCompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator); if (engine->hasException) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index d47393b3bb..c6a737b467 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -52,6 +52,7 @@ // #include <QtCore/qglobal.h> +#include <private/qv4compilerglobal_p.h> #include <QString> #ifdef QT_NO_DEBUG @@ -63,10 +64,6 @@ #include <qtqmlglobal.h> #include <private/qtqmlglobal_p.h> -#ifdef QT_QML_BOOTSTRAPPED -#define V4_BOOTSTRAP -#endif - #if defined(Q_CC_MSVC) #include <float.h> #include <math.h> @@ -82,53 +79,9 @@ inline bool isfinite(double d) { return _finite(d); } inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #endif -// Decide whether to enable or disable the JIT - -// White list architectures -// -// NOTE: This should match the logic in qv4targetplatform_p.h! - -#if defined(Q_PROCESSOR_X86_32) && (QT_POINTER_SIZE == 4) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD)) -# define V4_ENABLE_JIT -#elif defined(Q_PROCESSOR_X86_64) && (QT_POINTER_SIZE == 8) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)) -# define V4_ENABLE_JIT -#elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4) \ - && (defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_INTEGRITY)) -# if defined(thumb2) || defined(__thumb2__) || ((defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4) -# define V4_ENABLE_JIT -# elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2 // clang 3.5 and later will set this if the core supports the Thumb-2 ISA. -# define V4_ENABLE_JIT -# endif -#elif defined(Q_PROCESSOR_ARM_64) && (QT_POINTER_SIZE == 8) -# if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY) -# define V4_ENABLE_JIT -# endif -//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -//# define V4_ENABLE_JIT -#endif - -// check FPU with double precision on ARM platform -#if (defined(Q_PROCESSOR_ARM_64) || defined(Q_PROCESSOR_ARM_32)) && defined(V4_ENABLE_JIT) && defined(__ARM_FP) && (__ARM_FP <= 0x04) -# undef V4_ENABLE_JIT -#endif - -// Black list some platforms -#if defined(V4_ENABLE_JIT) -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -# undef V4_ENABLE_JIT -#endif -#endif - -// For debug purposes: add CONFIG+=force-compile-jit to qmake's command-line to always compile the JIT. -#if defined(V4_FORCE_COMPILE_JIT) && !defined(V4_ENABLE_JIT) -# define V4_ENABLE_JIT -#endif - // Do certain things depending on whether the JIT is enabled or disabled -#ifdef V4_ENABLE_JIT +#if QT_CONFIG(qml_jit) #define ENABLE_YARR_JIT 1 #define ENABLE_JIT 1 #define ENABLE_ASSEMBLER 1 @@ -255,11 +208,6 @@ struct SetMapObject; struct PromiseObject; struct PromiseCapability; -// ReturnedValue is used to return values from runtime methods -// the type has to be a primitive type (no struct or union), so that the compiler -// will return it in a register on all platforms. -// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm -typedef quint64 ReturnedValue; struct CallData; struct Scope; struct ScopedValue; @@ -280,20 +228,6 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -enum class ObservedTraceValues : quint8 { - Integer = 1 << 0, - Boolean = 1 << 1, - Double = 1 << 2, - Other = 1 << 3, - TypeMask = Integer | Boolean | Double | Other, - - TruePathTaken = 1 << 0, - FalsePathTaken = 1 << 1, - - ArrayWasAccessed = 1 << 7, - ArrayAccessNeededFallback = 1 << 6, -}; - enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, @@ -403,13 +337,6 @@ struct Q_QML_EXPORT StackFrame { }; typedef QVector<StackFrame> StackTrace; -enum class ObjectLiteralArgument { - Value, - Method, - Getter, - Setter -}; - namespace JIT { enum class CallResultDestination { diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index becdc3bc55..bb81fb52d4 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -49,18 +49,14 @@ #include "qv4string_p.h" #include "qv4jscall_p.h" -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> -#include <qv4codegen_p.h> +#include <private/qv4codegen_p.h> +#include <private/qv4alloca_p.h> #include "private/qlocale_tools_p.h" #include "private/qtools_p.h" #include <QtCore/QDebug> #include <QtCore/QString> #include <iostream> -#include "qv4alloca_p.h" #include <wtf/MathExtras.h> diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index 5db5bd46ec..c3d7165f71 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -39,29 +39,19 @@ #include "qv4identifier_p.h" #include "qv4identifiertable_p.h" #include "qv4string_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) : size(0) , numBits(numBits) , identifierTable(table) { - refCount.store(1); - alloc = primeForNumBits(numBits); + refCount.storeRelaxed(1); + alloc = qPrimeForNumBits(numBits); entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); identifierTable->addIdentifierHash(this); @@ -72,7 +62,7 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other) , numBits(other->numBits) , identifierTable(other->identifierTable) { - refCount.store(1); + refCount.storeRelaxed(1); alloc = other->alloc; entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); @@ -92,7 +82,7 @@ IdentifierHash::IdentifierHash(ExecutionEngine *engine) void IdentifierHash::detach() { - if (!d || d->refCount == 1) + if (!d || d->refCount.loadAcquire() == 1) return; IdentifierHashData *newData = new IdentifierHashData(d); if (d && !d->refCount.deref()) @@ -110,7 +100,7 @@ IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) if (grow) { ++d->numBits; - int newAlloc = primeForNumBits(d->numBits); + int newAlloc = qPrimeForNumBits(d->numBits); IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); for (int i = 0; i < d->alloc; ++i) { diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 4305bc4647..21b47c3909 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -38,28 +38,18 @@ ****************************************************************************/ #include "qv4identifiertable_p.h" #include "qv4symbol_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits) : engine(engine) , size(0) , numBits(numBits) { - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); memset(entriesByHash, 0, alloc*sizeof(Heap::String *)); @@ -87,7 +77,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str) if (grow) { ++numBits; - int newAlloc = primeForNumBits(numBits); + int newAlloc = qPrimeForNumBits(numBits); Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (uint i = 0; i < alloc; ++i) { @@ -216,9 +206,8 @@ PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str) Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const { - uint arrayIdx = i.asArrayIndex(); - if (arrayIdx < UINT_MAX) - return engine->newString(QString::number(arrayIdx)); + if (i.isArrayIndex()) + return engine->newString(QString::number(i.asArrayIndex())); if (!i.isValid()) return nullptr; diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 36569b0a60..92face6f94 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -72,13 +72,17 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, m_resultObject.set(v4, resultValue(v4)); #if QT_CONFIG(qml_network) - m_network = engine->v8Engine->networkAccessManager(); + if (QQmlEngine *qmlEngine = engine->qmlEngine()) { + m_network = qmlEngine->networkAccessManager(); - QNetworkRequest request; - request.setUrl(url); + QNetworkRequest request; + request.setUrl(url); - m_reply = m_network->get(request); - QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + } else { + finished(); + } #else finished(); #endif @@ -163,7 +167,6 @@ void QV4Include::finished() QByteArray data = m_reply->readAll(); QString code = QString::fromUtf8(data); - QmlIR::Document::removeScriptPragmas(code); QV4::Scoped<QV4::QmlContext> qml(scope, m_qmlContext.value()); QV4::Script script(v4, qml, /*parse as QML binding*/false, code, m_url.toString()); @@ -197,7 +200,7 @@ void QV4Include::finished() } /* - Documented in qv8engine.cpp + Documented in qv4engine.cpp */ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc) { @@ -214,7 +217,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons if (argc >= 2 && argv[1].as<QV4::FunctionObject>()) callbackFunction = argv[1]; -#if QT_CONFIG(qml_network) QUrl url(scope.engine->resolvedUrl(argv[0].toQStringNoThrow())); if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); @@ -225,9 +227,13 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons QV4::Scoped<QV4::QmlContext> qmlcontext(scope, scope.engine->qmlContext()); if (localFile.isEmpty()) { +#if QT_CONFIG(qml_network) QV4Include *i = new QV4Include(url, scope.engine, qmlcontext, callbackFunction); result = i->result(); - +#else + result = resultValue(scope.engine, NetworkError); + callback(callbackFunction, result); +#endif } else { QScopedPointer<QV4::Script> script; QString error; @@ -252,12 +258,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons callback(callbackFunction, result); } -#else - QV4::ScopedValue result(scope); - result = resultValue(scope.engine, NetworkError); - callback(callbackFunction, result); -#endif - return result->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index a10fda79f2..70849775cb 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -45,27 +45,18 @@ #include "qv4identifiertable_p.h" #include "qv4value_p.h" #include "qv4mm_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - PropertyHashData::PropertyHashData(int numBits) : refCount(1) , size(0) , numBits(numBits) { - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry)); memset(entries, 0, alloc*sizeof(PropertyHash::Entry)); } @@ -299,7 +290,6 @@ void InternalClass::init(ExecutionEngine *engine) void InternalClass::init(Heap::InternalClass *other) { Base::init(); - Q_ASSERT(!other->isFrozen); new (&propertyTable) PropertyHash(other->propertyTable); new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap); new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData); @@ -573,22 +563,6 @@ Heap::InternalClass *InternalClass::sealed() if (isSealed) return this; - bool alreadySealed = !extensible; - for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (attrs.isEmpty()) - continue; - if (attrs.isConfigurable()) { - alreadySealed = false; - break; - } - } - - if (alreadySealed) { - isSealed = true; - return this; - } - Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed }; Transition &t = lookupOrInsertTransition(temp); @@ -601,14 +575,15 @@ Heap::InternalClass *InternalClass::sealed() Scoped<QV4::InternalClass> ic(scope, engine->newClass(this)); Heap::InternalClass *s = ic->d(); - for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (attrs.isEmpty()) - continue; - attrs.setConfigurable(false); - s->propertyData.set(i, attrs); + if (!isFrozen) { // freezing also makes all properties non-configurable + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + attrs.setConfigurable(false); + s->propertyData.set(i, attrs); + } } - s->extensible = false; s->isSealed = true; t.lookup = s; @@ -620,28 +595,11 @@ Heap::InternalClass *InternalClass::frozen() if (isFrozen) return this; - bool alreadyFrozen = !extensible; - for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (attrs.isEmpty()) - continue; - if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) { - alreadyFrozen = false; - break; - } - } - - if (alreadyFrozen) { - isSealed = true; - isFrozen = true; - return this; - } - Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen); + Q_ASSERT(t.lookup && t.lookup->isFrozen); return t.lookup; } @@ -658,29 +616,42 @@ Heap::InternalClass *InternalClass::frozen() attrs.setConfigurable(false); f->propertyData.set(i, attrs); } - f->extensible = false; - f->isSealed = true; f->isFrozen = true; t.lookup = f; return f; } -Heap::InternalClass *InternalClass::propertiesFrozen() +InternalClass *InternalClass::canned() +{ + // scope the intermediate result to prevent it from getting garbage collected + Scope scope(engine); + Scoped<QV4::InternalClass> ic(scope, sealed()); + return ic->d()->nonExtensible(); +} + +InternalClass *InternalClass::cryopreserved() { + // scope the intermediate result to prevent it from getting garbage collected Scope scope(engine); - Scoped<QV4::InternalClass> frozen(scope, this); + Scoped<QV4::InternalClass> ic(scope, frozen()); + return ic->d()->canned(); +} + +bool InternalClass::isImplicitlyFrozen() const +{ + if (isFrozen) + return true; + for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (!nameMap.at(i).isValid()) + const PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) continue; - if (!attrs.isEmpty()) { - attrs.setWritable(false); - attrs.setConfigurable(false); - } - frozen = frozen->changeMember(nameMap.at(i), attrs); + if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) + return false; } - return frozen->d(); + + return true; } Heap::InternalClass *InternalClass::asProtoClass() diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 7bb10f47a3..403702ae55 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -432,7 +432,9 @@ struct InternalClass : Base { Q_REQUIRED_RESULT InternalClass *sealed(); Q_REQUIRED_RESULT InternalClass *frozen(); - Q_REQUIRED_RESULT InternalClass *propertiesFrozen(); + Q_REQUIRED_RESULT InternalClass *canned(); // sealed + nonExtensible + Q_REQUIRED_RESULT InternalClass *cryopreserved(); // frozen + sealed + nonExtensible + bool isImplicitlyFrozen() const; Q_REQUIRED_RESULT InternalClass *asProtoClass(); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 6b8fb509a8..0cda6b864a 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -467,11 +467,11 @@ bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, return false; } - if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) { + if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) { l->objectLookupTwoClasses.ic = first.objectLookup.ic; l->objectLookupTwoClasses.ic2 = second.objectLookup.ic; - l->objectLookupTwoClasses.offset = first.objectLookup.offset; - l->objectLookupTwoClasses.offset2 = second.objectLookup.offset; + l->objectLookupTwoClasses.offset = first.objectLookup.index; + l->objectLookupTwoClasses.offset2 = second.objectLookup.index; l->setter = setter0setter0; return true; } @@ -492,11 +492,11 @@ bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c return o->put(name, value); } -bool Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o && o->internalClass == l->objectLookup.ic) { - o->setProperty(engine, l->objectLookup.offset, value); + o->memberData->values.set(engine, l->objectLookup.offset, value); return true; } @@ -507,7 +507,7 @@ bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, co { Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o && o->internalClass == l->objectLookup.ic) { - o->setInlineProperty(engine, l->objectLookup.offset, value); + o->setInlinePropertyWithOffset(engine, l->objectLookup.offset, value); return true; } diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 4fd5e133f7..31c90b31f6 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -54,17 +54,14 @@ #include "qv4runtime_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" - -#if !defined(V4_BOOTSTRAP) #include "qv4object_p.h" #include "qv4internalclass_p.h" -#endif QT_BEGIN_NAMESPACE namespace QV4 { -struct Lookup { +struct Q_QML_PRIVATE_EXPORT Lookup { union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); @@ -81,8 +78,9 @@ struct Lookup { } markDef; struct { Heap::InternalClass *ic; - quintptr _unused; - int offset; + quintptr unused; + uint index; + uint offset; } objectLookup; struct { quintptr protoId; @@ -92,8 +90,8 @@ struct Lookup { struct { Heap::InternalClass *ic; Heap::InternalClass *ic2; - int offset; - int offset2; + uint offset; + uint offset2; } objectLookupTwoClasses; struct { quintptr protoId; @@ -111,12 +109,14 @@ struct Lookup { struct { Heap::InternalClass *newClass; quintptr protoId; - int offset; + uint offset; + uint unused; } insertionLookup; struct { quintptr _unused; quintptr _unused2; uint index; + uint unused; } indexedLookup; struct { Heap::InternalClass *ic; @@ -151,6 +151,19 @@ struct Lookup { quintptr reserved3; ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine); } qmlContextGlobalLookup; + struct { + Heap::Object *qmlTypeWrapper; + quintptr unused2; + } qmlTypeLookup; + struct { + Heap::InternalClass *ic; + quintptr unused; + ReturnedValue encodedEnumValue; + } qmlEnumValueLookup; + struct { + Heap::InternalClass *ic; + Heap::Object *qmlScopedEnumWrapper; + } qmlScopedEnumWrapperLookup; }; uint nameIndex; @@ -187,7 +200,7 @@ struct Lookup { static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index d85b30a056..4f22dc7330 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -54,7 +54,6 @@ #include "qv4value_p.h" #include "qv4enginebase_p.h" #include <private/qv4heap_p.h> -#include <private/qv4writebarrier_p.h> #include <private/qv4vtable_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp index 68741e7677..90e1908a84 100644 --- a/src/qml/jsruntime/qv4mapobject.cpp +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -80,7 +80,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, if (!adder) return scope.engine->throwTypeError(); - ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true)); if (scope.hasException()) return Encode::undefined(); Q_ASSERT(iter); @@ -89,7 +89,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, Value *arguments = scope.alloc(2); ScopedValue done(scope); forever { - done = Runtime::method_iteratorNext(scope.engine, iter, obj); + done = Runtime::IteratorNext::call(scope.engine, iter, obj); if (scope.hasException()) break; if (done->toBoolean()) @@ -112,7 +112,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, break; } ScopedValue falsey(scope, Encode(false)); - return Runtime::method_iteratorClose(scope.engine, iter, falsey); + return Runtime::IteratorClose::call(scope.engine, iter, falsey); } } return a->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index a60a49a811..6632d69c27 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -52,6 +52,7 @@ #include <qglobal.h> +#include <private/qv4staticvalue_p.h> #include <QtCore/qnumeric.h> #include <QtCore/private/qnumeric_p.h> #include <cmath> @@ -66,43 +67,28 @@ QT_BEGIN_NAMESPACE namespace QV4 { -static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); - return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); - return Value::fromInt32(result).asReturnedValue(); + if (Q_UNLIKELY(add_overflow(a, b, &result))) + return StaticValue::fromDouble(static_cast<double>(a) + b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); - return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); - return Value::fromInt32(result).asReturnedValue(); + if (Q_UNLIKELY(sub_overflow(a, b, &result))) + return StaticValue::fromDouble(static_cast<double>(a) - b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); - return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); - return Value::fromInt32(result).asReturnedValue(); + if (Q_UNLIKELY(mul_overflow(a, b, &result))) + return StaticValue::fromDouble(static_cast<double>(a) * b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp index 237ada8321..08a1900383 100644 --- a/src/qml/jsruntime/qv4module.cpp +++ b/src/qml/jsruntime/qv4module.cpp @@ -52,7 +52,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(Module); -void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit) +void Heap::Module::init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit) { Object::init(); @@ -106,7 +106,7 @@ void Module::evaluate() return; d()->evaluated = true; - CompiledData::CompilationUnit *unit = d()->unit; + ExecutableCompilationUnit *unit = d()->unit; unit->evaluateModuleRequests(); diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h index dca0678fe9..aabb2e005e 100644 --- a/src/qml/jsruntime/qv4module_p.h +++ b/src/qml/jsruntime/qv4module_p.h @@ -60,7 +60,7 @@ namespace QV4 { namespace Heap { #define ModuleMembers(class, Member) \ - Member(class, NoMark, CompiledData::CompilationUnit *, unit) \ + Member(class, NoMark, ExecutableCompilationUnit *, unit) \ Member(class, Pointer, CallContext *, scope) \ Member(class, HeapValue, HeapValue, self) \ Member(class, NoMark, bool, evaluated) @@ -68,7 +68,7 @@ namespace Heap { DECLARE_EXPORTED_HEAP_OBJECT(Module, Object) { DECLARE_MARKOBJECTS(Module) - void init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit); + void init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit); }; } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 02524b7da6..89161433ed 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -407,8 +407,8 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h { Heap::Object *o = d(); - uint index = id.asArrayIndex(); - if (index != UINT_MAX) { + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); Scope scope(this); PropertyAttributes attrs; ScopedProperty pd(scope); @@ -432,8 +432,6 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h break; } } else { - Q_ASSERT(!id.isArrayIndex()); - while (1) { auto idx = o->internalClass->findValueOrGetter(id); if (idx.isValid()) { @@ -471,14 +469,13 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) { // This object standard methods in the vtable, so we can take a shortcut // and avoid the calls to getOwnProperty and defineOwnProperty - uint index = id.asArrayIndex(); PropertyAttributes attrs; PropertyIndex propertyIndex{nullptr, nullptr}; - if (index != UINT_MAX) { + if (id.isArrayIndex()) { if (arrayData()) - propertyIndex = arrayData()->getValueOrSetter(index, &attrs); + propertyIndex = arrayData()->getValueOrSetter(id.asArrayIndex(), &attrs); } else { auto member = internalClass()->findValueOrSetter(id); if (member.isValid()) { @@ -547,12 +544,11 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) { // standard object, we can avoid some more checks - uint index = id.asArrayIndex(); - if (index == UINT_MAX) { + if (id.isArrayIndex()) { + r->arraySet(id.asArrayIndex(), value); + } else { ScopedStringOrSymbol s(scope, id.asStringOrSymbol()); r->insertMember(s, value); - } else { - r->arraySet(index, value); } return true; } @@ -787,8 +783,15 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, return lookup->setter(lookup, engine, *object, value); } else if (idx.attrs.isData() && idx.attrs.isWritable()) { lookup->objectLookup.ic = object->internalClass(); - lookup->objectLookup.offset = idx.index; - lookup->setter = idx.index < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; + lookup->objectLookup.index = idx.index; + const auto nInline = object->d()->vtable()->nInlineProperties; + if (idx.index < nInline) { + lookup->setter = Lookup::setter0Inline; + lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset; + } else { + lookup->setter = Lookup::setter0MemberData; + lookup->objectLookup.offset = idx.index - nInline; + } return lookup->setter(lookup, engine, *object, value); } else { // ### handle setter diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index bee4aadafe..f3375929a3 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -79,21 +79,21 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { } const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const { - Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties); + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties)); return reinterpret_cast<const Value *>(this) + indexWithOffset; } const Value *inlinePropertyData(uint index) const { Q_ASSERT(index < vtable()->nInlineProperties); return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; } - void setInlineProperty(ExecutionEngine *e, uint index, Value v) { - Q_ASSERT(index < vtable()->nInlineProperties); - Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Value v) { + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties)); + Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset; WriteBarrier::write(e, this, prop->data_ptr(), v.asReturnedValue()); } - void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) { - Q_ASSERT(index < vtable()->nInlineProperties); - Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Heap::Base *b) { + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties)); + Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset; WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } @@ -115,7 +115,7 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { void setProperty(ExecutionEngine *e, uint index, Value v) { uint nInline = vtable()->nInlineProperties; if (index < nInline) { - setInlineProperty(e, index, v); + setInlinePropertyWithOffset(e, index + vtable()->inlinePropertyOffset, v); return; } index -= nInline; @@ -124,7 +124,7 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) { uint nInline = vtable()->nInlineProperties; if (index < nInline) { - setInlineProperty(e, index, b); + setInlinePropertyWithOffset(e, index + vtable()->inlinePropertyOffset, b); return; } index -= nInline; @@ -410,7 +410,7 @@ private: friend struct ObjectPrototype; }; -struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator +struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator { uint arrayIndex = 0; uint memberIndex = 0; @@ -543,13 +543,11 @@ inline const ArrayObject *Value::as() const { return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr; } -#ifndef V4_BOOTSTRAP template<> inline ReturnedValue value_convert<Object>(ExecutionEngine *e, const Value &v) { return v.toObject(e)->asReturnedValue(); } -#endif } diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 6b4c3ba71a..3d3b3f413f 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -422,7 +422,7 @@ ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value Scope scope(b); ScopedObject o(scope, a); - o->setInternalClass(o->internalClass()->sealed()); + o->setInternalClass(o->internalClass()->canned()); if (o->arrayData()) { ArrayData::ensureAttributes(o); @@ -448,7 +448,7 @@ ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Valu if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); - o->setInternalClass(o->internalClass()->frozen()); + o->setInternalClass(o->internalClass()->cryopreserved()); if (o->arrayData()) { ArrayData::ensureAttributes(o); @@ -489,7 +489,7 @@ ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Va if (o->isExtensible()) return Encode(false); - if (o->internalClass() != o->internalClass()->sealed()) + if (o->internalClass() != o->internalClass()->canned()) return Encode(false); if (!o->arrayData() || !o->arrayData()->length()) @@ -521,7 +521,7 @@ ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Va if (o->isExtensible()) return Encode(false); - if (o->internalClass() != o->internalClass()->frozen()) + if (!o->internalClass()->isImplicitlyFrozen()) return Encode(false); if (!o->arrayData() || !o->arrayData()->length()) diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index e9515b5b68..8707305dc2 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -74,7 +74,7 @@ struct ObjectCtor: FunctionObject static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; -struct ObjectPrototype: Object +struct Q_QML_PRIVATE_EXPORT ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index b337243204..26e1074fe3 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -49,7 +49,7 @@ namespace Profiling { FunctionLocation FunctionCall::resolveLocation() const { return FunctionLocation(m_function->name()->toQString(), - m_function->compilationUnit->fileName(), + m_function->executableCompilationUnit()->fileName(), m_function->compiledFunction->location.line, m_function->compiledFunction->location.column); } diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 8461384e9a..ccf7c9210d 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -144,19 +144,19 @@ public: FunctionCall(Function *function, qint64 start, qint64 end) : m_function(function), m_start(start), m_end(end) - { m_function->compilationUnit->addref(); } + { m_function->executableCompilationUnit()->addref(); } FunctionCall(const FunctionCall &other) : m_function(other.m_function), m_start(other.m_start), m_end(other.m_end) - { m_function->compilationUnit->addref(); } + { m_function->executableCompilationUnit()->addref(); } ~FunctionCall() - { m_function->compilationUnit->release(); } + { m_function->executableCompilationUnit()->release(); } FunctionCall &operator=(const FunctionCall &other) { if (&other != this) { - other.m_function->compilationUnit->addref(); - m_function->compilationUnit->release(); + other.m_function->executableCompilationUnit()->addref(); + m_function->executableCompilationUnit()->release(); m_function = other.m_function; m_start = other.m_start; m_end = other.m_end; @@ -189,22 +189,22 @@ public: SentMarker(const SentMarker &other) : m_function(other.m_function) { if (m_function) - m_function->compilationUnit->addref(); + m_function->executableCompilationUnit()->addref(); } ~SentMarker() { if (m_function) - m_function->compilationUnit->release(); + m_function->executableCompilationUnit()->release(); } SentMarker &operator=(const SentMarker &other) { if (&other != this) { if (m_function) - m_function->compilationUnit->release(); + m_function->executableCompilationUnit()->release(); m_function = other.m_function; - m_function->compilationUnit->addref(); + m_function->executableCompilationUnit()->addref(); } return *this; } @@ -213,7 +213,7 @@ public: { Q_ASSERT(m_function == nullptr); m_function = function; - m_function->compilationUnit->addref(); + m_function->executableCompilationUnit()->addref(); } bool isValid() const diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp index 851fee7bd8..17d218a6eb 100644 --- a/src/qml/jsruntime/qv4promiseobject.cpp +++ b/src/qml/jsruntime/qv4promiseobject.cpp @@ -558,7 +558,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this ScopedFunctionObject reject(scope, capability->d()->reject); ScopedObject itemsObject(scope, argv); - ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true)); + ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true)); if (!iteratorObject || scope.hasException()) { ScopedObject error(scope); if (scope.hasException()) { @@ -583,7 +583,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this for (;;) { Scope scope(e); ScopedValue nextValue(scope); - doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue)); + doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue)); if (doneValue->toBoolean()) break; @@ -611,7 +611,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -619,7 +619,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1))); if (!nextPromise || scope.hasException()) { - ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue)); + ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -641,7 +641,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -660,7 +660,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this dropException(e); if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -708,7 +708,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi ScopedFunctionObject reject(scope, capability->d()->reject); ScopedObject itemsObject(scope, argv); - ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true)); + ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true)); if (!iteratorObject) { ScopedObject error(scope, e->newTypeErrorObject(QStringLiteral("Type error"))); reject->call(newPromise, error, 1); @@ -719,10 +719,10 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi for (;;) { Scope scope(e); ScopedValue nextValue(scope); - doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue)); + doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue)); if (scope.hasException()) { - ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue)); + ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -757,7 +757,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -765,7 +765,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1))); if (!nextPromise || scope.hasException()) { - ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue)); + ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -785,7 +785,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -804,7 +804,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi dropException(e); if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h index 47867765db..b2a2ec3dea 100644 --- a/src/qml/jsruntime/qv4propertykey_p.h +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -113,8 +113,8 @@ public: static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; } static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; } bool isStringOrSymbol() const { return isManaged() && val != 0; } - uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); } - uint isArrayIndex() const { return !isManaged() && val != 0 && static_cast<uint>(val & 0xffffffff) != std::numeric_limits<uint>::max(); } + uint asArrayIndex() const { Q_ASSERT(isArrayIndex()); return static_cast<uint>(val & 0xffffffff); } + uint isArrayIndex() const { return !isManaged() && val != 0; } bool isValid() const { return val != 0; } static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b) { PropertyKey key; key.setM(b); return key; } @@ -124,7 +124,7 @@ public: return m(); } - bool isString() const; + Q_QML_EXPORT bool isString() const; bool isSymbol() const; bool isCanonicalNumericIndexString() const; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 969d44ecf6..e2d3b98ff6 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qv4qmlcontext_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlcontext_p.h> @@ -232,28 +231,32 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } else if (r.type.isValid()) { if (lookup) { if (r.type.isSingleton()) { - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); - siinfo->init(e); - if (siinfo->qobjectApi(e)) { + QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) { + e->singletonInstance<QObject*>(r.type); lookup->qmlContextSingletonLookup.singleton = static_cast<Heap::Object*>( Value::fromReturnedValue( QQmlTypeWrapper::create(v4, nullptr, r.type) ).heapObject()); } else { - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); + QJSValue singleton = e->singletonInstance<QJSValue>(r.type); + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, singleton)); lookup->qmlContextSingletonLookup.singleton = o->d(); } lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; return lookup->qmlContextPropertyGetter(lookup, v4, base); } } - return QQmlTypeWrapper::create(v4, scopeObject, r.type); + result = QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { - return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); + result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); } - Q_ASSERT(!"Unreachable"); + if (lookup) { + lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject()); + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType; + } + return result->asReturnedValue(); } // Fall through @@ -660,6 +663,27 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec return Encode::undefined(); } +ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QObject *scopeObject = qmlContext->qmlScope(); + if (scopeObject && QQmlData::wasDeleted(scopeObject)) + return QV4::Encode::undefined(); + + Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper; + if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) { + l->qmlTypeLookup.qmlTypeWrapper = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + } + + return Value::fromHeapObject(heapObject).asReturnedValue(); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 4c8287ef2f..e3e7239fe5 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 63b6435112..9d0b65cc0a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -39,7 +39,7 @@ #include "qv4qobjectwrapper_p.h" -#include <private/qqmlpropertycache_p.h> +#include <private/qqmlstaticmetaobject_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlbinding_p.h> @@ -50,7 +50,6 @@ #include <private/qqmlvaluetypewrapper_p.h> #include <private/qqmllistwrapper_p.h> #include <private/qqmlbuiltinfunctions_p.h> -#include <private/qv8engine_p.h> #include <private/qv4arraybuffer_p.h> #include <private/qv4functionobject_p.h> @@ -58,6 +57,7 @@ #include <private/qv4variantobject_p.h> #include <private/qv4identifiertable_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4qmlcontext_p.h> #if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> @@ -81,7 +81,9 @@ #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> #include <QtCore/qmetaobject.h> +#if QT_CONFIG(qml_itemmodel) #include <QtCore/qabstractitemmodel.h> +#endif #include <QtCore/qloggingcategory.h> #include <vector> @@ -165,10 +167,6 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object double v = 0; property.readProperty(object, &v); return QV4::Encode(v); - } else if (property.isV4Handle()) { - QQmlV4Handle handle; - property.readProperty(object, &handle); - return handle; } else if (property.propType() == qMetaTypeId<QJSValue>()) { QJSValue v; property.readProperty(object, &v); @@ -768,6 +766,8 @@ struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator ~QObjectWrapperOwnPropertyKeyIterator() override = default; PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; +private: + QSet<QByteArray> m_alreadySeen; }; PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) @@ -808,6 +808,11 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro ++propertyIndex; if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2))) continue; + // filter out duplicates due to overloads: + if (m_alreadySeen.contains(method.name())) + continue; + else + m_alreadySeen.insert(method.name()); ExecutionEngine *thatEngine = that->engine(); Scope scope(thatEngine); ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name()))); @@ -1193,15 +1198,14 @@ DEFINE_OBJECT_VTABLE(QObjectWrapper); namespace { -template<typename A, typename B, typename C, typename D, typename E, - typename F, typename G, typename H> -class MaxSizeOf8 { +template<typename A, typename B, typename C, typename D, typename E, typename F, typename G> +class MaxSizeOf7 { template<typename Z, typename X> struct SMax { char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)]; }; public: - static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >); + static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >); }; struct CallArgument { @@ -1232,16 +1236,17 @@ private: std::vector<bool> *stdVectorBoolPtr; std::vector<QString> *stdVectorQStringPtr; std::vector<QUrl> *stdVectorQUrlPtr; +#if QT_CONFIG(qml_itemmodel) std::vector<QModelIndex> *stdVectorQModelIndexPtr; +#endif - char allocData[MaxSizeOf8<QVariant, - QString, - QList<QObject *>, - QJSValue, - QQmlV4Handle, - QJsonArray, - QJsonObject, - QJsonValue>::Size]; + char allocData[MaxSizeOf7<QVariant, + QString, + QList<QObject *>, + QJSValue, + QJsonArray, + QJsonObject, + QJsonValue>::Size]; qint64 q_for_alignment; }; @@ -1252,7 +1257,6 @@ private: QVariant *qvariantPtr; QList<QObject *> *qlistPtr; QJSValue *qjsValuePtr; - QQmlV4Handle *handlePtr; QJsonArray *jsonArrayPtr; QJsonObject *jsonObjectPtr; QJsonValue *jsonValuePtr; @@ -1271,7 +1275,8 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index QVarLengthArray<CallArgument, 9> args(argCount + 1); args[0].initAsType(returnType); for (int ii = 0; ii < argCount; ++ii) { - if (!args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii])) { + if (!args[ii + 1].fromValue(argTypes[ii], engine, + callArgs->args[ii].asValue<QV4::Value>())) { qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii); const StackTrace stack = engine->stackTrace(); for (const StackFrame &frame : stack) { @@ -1383,6 +1388,9 @@ static int MatchScore(const QV4::Value &actual, int conversionType) } else if (actual.as<QV4::RegExpObject>()) { switch (conversionType) { case QMetaType::QRegExp: +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: +#endif return 0; default: return 10; @@ -1613,8 +1621,10 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const continue; // We already have a better option int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])), + methodArgTypes[ii]); + } if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { best = *attempt; @@ -1688,8 +1698,10 @@ void *CallArgument::dataPtr() return stdVectorQStringPtr; else if (type == qMetaTypeId<std::vector<QUrl>>()) return stdVectorQUrlPtr; +#if QT_CONFIG(qml_itemmodel) else if (type == qMetaTypeId<std::vector<QModelIndex>>()) return stdVectorQModelIndexPtr; +#endif else if (type != 0) return (void *)&allocData; return nullptr; @@ -1721,9 +1733,6 @@ void CallArgument::initAsType(int callType) } else if (callType == qMetaTypeId<QList<QObject *> >()) { type = callType; qlistPtr = new (&allocData) QList<QObject *>(); - } else if (callType == qMetaTypeId<QQmlV4Handle>()) { - type = callType; - handlePtr = new (&allocData) QQmlV4Handle; } else if (callType == QMetaType::QJsonArray) { type = callType; jsonArrayPtr = new (&allocData) QJsonArray(); @@ -1824,9 +1833,6 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q return false; } } - } else if (callType == qMetaTypeId<QQmlV4Handle>()) { - handlePtr = new (&allocData) QQmlV4Handle(value.asReturnedValue()); - type = callType; } else if (callType == QMetaType::QJsonArray) { QV4::ScopedArrayObject a(scope, value); jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a)); @@ -1846,7 +1852,10 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q || callType == qMetaTypeId<std::vector<bool>>() || callType == qMetaTypeId<std::vector<QString>>() || callType == qMetaTypeId<std::vector<QUrl>>() - || callType == qMetaTypeId<std::vector<QModelIndex>>()) { +#if QT_CONFIG(qml_itemmodel) + || callType == qMetaTypeId<std::vector<QModelIndex>>() +#endif + ) { queryEngine = true; const QV4::Object* object = value.as<QV4::Object>(); if (callType == qMetaTypeId<std::vector<int>>()) { @@ -1864,9 +1873,11 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q } else if (callType == qMetaTypeId<std::vector<QUrl>>()) { stdVectorQUrlPtr = nullptr; fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine); +#if QT_CONFIG(qml_itemmodel) } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) { stdVectorQModelIndexPtr = nullptr; fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); +#endif } #endif } else if (QMetaType::typeFlags(callType) @@ -1951,8 +1962,6 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii)))); array->setArrayLengthUnchecked(list.count()); return array.asReturnedValue(); - } else if (type == qMetaTypeId<QQmlV4Handle>()) { - return *handlePtr; } else if (type == QMetaType::QJsonArray) { return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr); } else if (type == QMetaType::QJsonObject) { @@ -2258,8 +2267,10 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine continue; // We already have a better option int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])), + methodArgTypes[ii]); + } if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { best = attempt; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 5543c4d5a6..ac9cad2bdb 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -55,9 +55,7 @@ #include <QtCore/qmetatype.h> #include <QtCore/qpair.h> #include <QtCore/qhash.h> -#include <private/qhashedstring_p.h> #include <private/qqmldata_p.h> -#include <private/qqmlpropertycache_p.h> #include <private/qintrusivelist_p.h> #include <private/qv4value_p.h> diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index 15dcb602eb..0772770d63 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -148,7 +148,7 @@ ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Valu if (!argc || !argv[0].isObject()) return e->throwTypeError(); - bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue()); + bool result = Runtime::DeleteProperty_NoThrow::call(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue()); return Encode(result); } diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 39a2e96b45..c1a42c4afa 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -50,10 +50,13 @@ #include <QtCore/QDebug> #include <QtCore/qregexp.h> +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif #include <cassert> #include <typeinfo> #include <iostream> -#include "qv4alloca_p.h" +#include <private/qv4alloca_p.h> QT_BEGIN_NAMESPACE @@ -134,6 +137,25 @@ void Heap::RegExpObject::init(const QRegExp &re) o->initProperties(); } +#if QT_CONFIG(regularexpression) +// Converts a QRegularExpression to a JS RegExp. +// The conversion is not 100% exact since ECMA regexp and QRegularExpression +// have different semantics/flags, but we try to do our best. +void Heap::RegExpObject::init(const QRegularExpression &re) +{ + Object::init(); + + Scope scope(internalClass->engine); + Scoped<QV4::RegExpObject> o(scope, this); + + const uint flags = (re.patternOptions() & QRegularExpression::CaseInsensitiveOption) + ? CompiledData::RegExp::RegExp_IgnoreCase + : CompiledData::RegExp::RegExp_NoFlags; + o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, re.pattern(), flags)); + o->initProperties(); +} +#endif + void RegExpObject::initProperties() { setProperty(Index_LastIndex, Value::fromInt32(0)); @@ -150,6 +172,20 @@ QRegExp RegExpObject::toQRegExp() const return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2); } +#if QT_CONFIG(regularexpression) +// Converts a JS RegExp to a QRegularExpression. +// The conversion is not 100% exact since ECMA regexp and QRegularExpression +// have different semantics/flags, but we try to do our best. +QRegularExpression RegExpObject::toQRegularExpression() const +{ + QRegularExpression::PatternOptions caseSensitivity + = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) + ? QRegularExpression::CaseInsensitiveOption + : QRegularExpression::NoPatternOption; + return QRegularExpression(*value()->pattern, caseSensitivity); +} +#endif + QString RegExpObject::toString() const { QString p = *value()->pattern; @@ -162,13 +198,6 @@ QString RegExpObject::toString() const return p; } -QString RegExpObject::source() const -{ - Scope scope(engine()); - ScopedValue s(scope, get(scope.engine->id_source())); - return s->toQString(); -} - ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str) { QString s = str->toQString(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index a584404c0b..b94889e9f0 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -81,6 +81,9 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) { void init(); void init(QV4::RegExp *value); void init(const QRegExp &re); +#if QT_CONFIG(regularexpression) + void init(const QRegularExpression &re); +#endif }; #define RegExpCtorMembers(class, Member) \ @@ -98,7 +101,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { } -struct RegExpObject: Object { +struct Q_QML_PRIVATE_EXPORT RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) V4_INTERNALCLASS(RegExpObject) @@ -138,8 +141,16 @@ struct RegExpObject: Object { } QRegExp toQRegExp() const; +#if QT_CONFIG(regularexpression) + QRegularExpression toQRegularExpression() const; +#endif QString toString() const; - QString source() const; + QString source() const + { + Scope scope(engine()); + ScopedValue s(scope, get(scope.engine->id_source())); + return s->toQString(); + } Heap::RegExp *value() const { return d()->value; } uint flags() const { return d()->value->flags; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index f7c339dc26..aaa198c62a 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -38,9 +38,8 @@ ****************************************************************************/ #include "qv4global_p.h" -#include "qv4engine_p.h" #include "qv4runtime_p.h" -#ifndef V4_BOOTSTRAP +#include "qv4engine_p.h" #include "qv4object_p.h" #include "qv4objectproto_p.h" #include "qv4globalobject_p.h" @@ -60,11 +59,10 @@ #include <private/qqmltypewrapper_p.h> #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> +#include <private/qqmljsast_p.h> #include "qv4qobjectwrapper_p.h" #include "qv4symbol_p.h" #include "qv4generatorobject_p.h" -#include <private/qv8engine_p.h> -#endif #include <QtCore/QDebug> #include <cassert> @@ -223,13 +221,9 @@ void RuntimeCounters::count(const char *func, uint tag1, uint tag2) #endif // QV4_COUNT_RUNTIME_FUNCTIONS -#ifndef V4_BOOTSTRAP - -Runtime::Runtime() +static QV4::Lookup *runtimeLookup(Function *f, uint i) { -#define INIT_METHOD(returnvalue, name, args) runtimeMethods[name] = reinterpret_cast<void*>(&method_##name); -FOR_EACH_RUNTIME_METHOD(INIT_METHOD) -#undef INIT_METHOD + return f->executableCompilationUnit()->runtimeLookups + i; } void RuntimeHelpers::numberToString(QString *result, double num, int radix) @@ -320,9 +314,10 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->prepend(QLatin1Char('-')); } -ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) +ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() + ->runtimeFunctions[functionId]; Q_ASSERT(clos); ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); if (clos->isGenerator()) @@ -330,7 +325,7 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index) +Bool Runtime::DeleteProperty_NoThrow::call(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); ScopedObject o(scope, base.toObject(engine)); @@ -344,14 +339,36 @@ bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, return o->deleteProperty(key); } -bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::DeleteProperty::call(ExecutionEngine *engine, QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +{ + if (!Runtime::DeleteProperty_NoThrow::call(engine, base, index)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name); } -QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) +ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name) +{ + if (!Runtime::DeleteName_NoThrow::call(engine, name)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval) { // 11.8.6, 5: rval must be an Object const Object *rhs = rval.as<Object>(); @@ -376,7 +393,7 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val return Encode(result->toBoolean()); } -QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right) { Object *ro = right.objectValue(); if (!ro) @@ -604,13 +621,12 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -ReturnedValue RuntimeHelpers::getTemplateObject(Function *function, int index) +ReturnedValue Runtime::GetTemplateObject::call(Function *function, int index) { - return function->compilationUnit->templateObjectAt(index)->asReturnedValue(); + return function->executableCompilationUnit()->templateObjectAt(index)->asReturnedValue(); } - -void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) +void Runtime::StoreProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); QV4::Function *v4Function = engine->currentStackFrame->v4Function; @@ -683,7 +699,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, return o->get(name); } -ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) +ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &object, const Value &index) { if (index.isPositiveInt()) { uint idx = static_cast<uint>(index.int_32()); @@ -704,30 +720,6 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value & return getElementFallback(engine, object, index); } -ReturnedValue Runtime::method_loadElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) - if (!s->data(idx).isEmpty()) - return s->data(idx).asReturnedValue(); - } - } - } - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementIntFallback(engine, object, idx); - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementFallback(engine, object, index); -} - static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); @@ -761,7 +753,7 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val return o->put(name, value); } -void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { if (index.isPositiveInt()) { uint idx = static_cast<uint>(index.int_32()); @@ -783,31 +775,7 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, engine->throwTypeError(); } -void Runtime::method_storeElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, value); - return; - } - } - } - } - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) - engine->throwTypeError(); -} - -ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) +ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); ScopedObject o(scope, (Object *)nullptr); @@ -830,7 +798,7 @@ ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value & return engine->newForInIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value) +ReturnedValue Runtime::IteratorNext::call(ExecutionEngine *engine, const Value &iterator, Value *value) { // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true // and the stack unwinding won't close the iterator @@ -866,7 +834,7 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value return Encode(false); } -ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) +ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) { // the return value encodes how to continue the yield* iteration. // true implies iteration is done, false for iteration to continue @@ -903,7 +871,7 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, if (t->isUndefined()) { // no throw method on the iterator ScopedValue done(scope, Encode(false)); - method_iteratorClose(engine, iterator, done); + IteratorClose::call(engine, iterator, done); if (engine->hasException) return Encode::undefined(); return engine->throwTypeError(); @@ -938,7 +906,7 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, return Encode(false); } -ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done) +ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator, const Value &done) { Q_ASSERT(iterator.isObject()); Q_ASSERT(done.isBoolean()); @@ -978,7 +946,7 @@ ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value return originalCompletion(); } -ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator) +ReturnedValue Runtime::DestructureRestElement::call(ExecutionEngine *engine, const Value &iterator) { Q_ASSERT(iterator.isObject()); @@ -988,7 +956,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co uint index = 0; while (1) { ScopedValue n(scope); - ScopedValue done(scope, method_iteratorNext(engine, iterator, n)); + ScopedValue done(scope, IteratorNext::call(engine, iterator, n)); if (engine->hasException) return Encode::undefined(); Q_ASSERT(done->isBoolean()); @@ -1000,7 +968,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co return array->asReturnedValue(); } -void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::StoreNameSloppy::call(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1010,7 +978,7 @@ void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, con engine->globalObject->put(name, value); } -void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::StoreNameStrict::call(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1021,7 +989,7 @@ void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, con engine->throwReferenceError(name); } -ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex) +ReturnedValue Runtime::LoadProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1041,7 +1009,7 @@ ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value return o->get(name); } -ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1055,7 +1023,8 @@ static Object *getSuperBase(Scope &scope) return nullptr; } - ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function); + ScopedFunctionObject f( + scope, Value::fromStaticValue(scope.engine->currentStackFrame->jsFrame->function)); ScopedObject homeObject(scope, f->getHomeObject()); if (!homeObject) { ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context)); @@ -1084,7 +1053,7 @@ static Object *getSuperBase(Scope &scope) return proto; } -ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property) +ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Value &property) { Scope scope(engine); Object *base = getSuperBase(scope); @@ -1093,10 +1062,11 @@ ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const V ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); - return base->get(key, &engine->currentStackFrame->jsFrame->thisObject); + return base->get( + key, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); } -void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value) +void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value) { Scope scope(engine); Object *base = getSuperBase(scope); @@ -1105,12 +1075,46 @@ void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &pr ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return; - bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject); + bool result = base->put( + key, value, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); if (!result && engine->currentStackFrame->v4Function->isStrict()) engine->throwTypeError(); } -ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, const Value &t) +ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function *f, int index) +{ + Lookup *l = runtimeLookup(f, index); + return l->globalGetter(l, engine); +} + +ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index) +{ + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); + return l->qmlContextPropertyGetter(l, engine, nullptr); +} + +ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index) +{ + Lookup *l = runtimeLookup(f, index); + return l->getter(l, engine, base); +} + +void Runtime::SetLookupSloppy::call(Function *f, const Value &base, int index, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = runtimeLookup(f, index); + l->setter(l, engine, const_cast<Value &>(base), value); +} + +void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = runtimeLookup(f, index); + if (!l->setter(l, engine, const_cast<Value &>(base), value)) + engine->throwTypeError(); +} + +ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t) { if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) { return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number @@ -1124,8 +1128,6 @@ ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, cons return c->asReturnedValue(); } -#endif // V4_BOOTSTRAP - uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) { Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString()))); @@ -1143,25 +1145,21 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) double dx = RuntimeHelpers::toNumber(x); return dx == y.asDouble(); } else if (x.isBoolean()) { - return Runtime::method_compareEqual(Value::fromDouble((double) x.booleanValue()), y); + return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y); } else if (y.isBoolean()) { - return Runtime::method_compareEqual(x, Value::fromDouble((double) y.booleanValue())); + return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue())); } else { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else Object *xo = x.objectValue(); Object *yo = y.objectValue(); if (yo && (x.isNumber() || x.isString())) { Scope scope(yo->engine()); ScopedValue py(scope, RuntimeHelpers::objectDefaultValue(yo, PREFERREDTYPE_HINT)); - return Runtime::method_compareEqual(x, py); + return Runtime::CompareEqual::call(x, py); } else if (xo && (y.isNumber() || y.isString())) { Scope scope(xo->engine()); ScopedValue px(scope, RuntimeHelpers::objectDefaultValue(xo, PREFERREDTYPE_HINT)); - return Runtime::method_compareEqual(px, y); + return Runtime::CompareEqual::call(px, y); } -#endif } return false; @@ -1177,12 +1175,13 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y) if (x.isNumber()) return y.isNumber() && x.asDouble() == y.asDouble(); - if (x.isManaged()) + if (x.isManaged()) { return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>()); + } return false; } -QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) +QV4::Bool Runtime::CompareGreaterThan::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1192,26 +1191,17 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareGreaterThan(pl, pr); -#endif + return Runtime::CompareGreaterThan::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1219,7 +1209,7 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) return dl > dr; } -QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) +QV4::Bool Runtime::CompareLessThan::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1229,26 +1219,17 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareLessThan(pl, pr); -#endif + return Runtime::CompareLessThan::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1256,7 +1237,7 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) return dl < dr; } -QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) +QV4::Bool Runtime::CompareGreaterEqual::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1266,26 +1247,17 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareGreaterEqual(pl, pr); -#endif + return Runtime::CompareGreaterEqual::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1293,7 +1265,7 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) return dl >= dr; } -QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) +QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1303,26 +1275,17 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareLessEqual(pl, pr); -#endif + return Runtime::CompareLessEqual::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1330,22 +1293,21 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) return dl <= dr; } -#ifndef V4_BOOTSTRAP -Bool Runtime::method_compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right) +Bool Runtime::CompareInstanceof::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, method_instanceof(engine, left, right)); + ScopedValue v(scope, Instanceof::call(engine, left, right)); return v->booleanValue(); } -uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const Value &right) +uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, method_in(engine, left, right)); + ScopedValue v(scope, In::call(engine, left, right)); return v->booleanValue(); } @@ -1359,33 +1321,36 @@ static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engin return engine->throwTypeError(msg); } -ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc) { Scope scope(engine); - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); Value thisObject = Value::undefinedValue(); - if (!function.isFunctionObject()) + if (!function.isFunctionObject()) { return throwPropertyIsNotAFunctionTypeError(engine, &thisObject, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + } return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); } -ReturnedValue Runtime::method_callQmlContextPropertyLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index, + Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject)); - if (!function.isFunctionObject()) + if (!function.isFunctionObject()) { return throwPropertyIsNotAFunctionTypeError(engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + } return static_cast<FunctionObject &>(function).call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc) +ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); @@ -1404,7 +1369,7 @@ ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Va return function->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) +ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); @@ -1415,17 +1380,22 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V if (engine->hasException) return Encode::undefined(); - if (!f) - return throwPropertyIsNotAFunctionTypeError(engine, thisObject, - engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString()); + if (!f) { + return throwPropertyIsNotAFunctionTypeError( + engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit + ->runtimeStrings[nameIndex]->toQString()); + } return f->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) +ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &baseRef, int nameIndex, Value *argv, int argc) { + const Value *base = &baseRef; Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name( + scope, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject lookupObject(scope, base); if (!lookupObject) { @@ -1437,7 +1407,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, } if (base->isManaged()) { - Managed *m = static_cast<Managed *>(base); + const Managed *m = static_cast<const Managed *>(base); lookupObject = m->internalClass()->prototype; Q_ASSERT(m->internalClass()->prototype); } else { @@ -1461,20 +1431,21 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, return f->call(base, argv, argc); } -ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const Value &base, uint index, Value *argv, int argc) { - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); // ok to have the value on the stack here - Value f = Value::fromReturnedValue(l->getter(l, engine, *base)); + Value f = Value::fromReturnedValue(l->getter(l, engine, base)); if (!f.isFunctionObject()) return engine->throwTypeError(); - return static_cast<FunctionObject &>(f).call(base, argv, argc); + return static_cast<FunctionObject &>(f).call(&base, argv, argc); } -ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc) +ReturnedValue Runtime::CallElement::call(ExecutionEngine *engine, const Value &baseRef, const Value &index, Value *argv, int argc) { + const Value *base = &baseRef; Scope scope(engine); ScopedValue thisObject(scope, base->toObject(engine)); base = thisObject; @@ -1483,14 +1454,14 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, if (engine->hasException) return Encode::undefined(); - ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str)); + ScopedFunctionObject f(scope, static_cast<const Object *>(base)->get(str)); if (!f) return engine->throwTypeError(); return f->call(base, argv, argc); } -ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc) +ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc) { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); @@ -1498,11 +1469,12 @@ ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(func).call(&undef, argv, argc); } -ReturnedValue Runtime::method_callWithReceiver(ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc) +ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Value &func, + const Value &thisObject, Value argv[], int argc) { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - return static_cast<const FunctionObject &>(func).call(thisObject, argv, argc); + return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc); } struct CallArgs { @@ -1528,11 +1500,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) } // spread element ++i; - it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1); + it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1); if (scope.engine->hasException) return { nullptr, 0 }; while (1) { - done = Runtime::method_iteratorNext(scope.engine, it, v); + done = Runtime::IteratorNext::call(scope.engine, it, v); if (scope.engine->hasException) return { nullptr, 0 }; Q_ASSERT(done->isBoolean()); @@ -1545,7 +1517,7 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) return { arguments, argCount }; } -ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) +ReturnedValue Runtime::CallWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) { Q_ASSERT(argc >= 1); if (!function.isFunctionObject()) @@ -1559,7 +1531,7 @@ ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Valu return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc); } -ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +ReturnedValue Runtime::Construct::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); @@ -1567,7 +1539,7 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget); } -ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); @@ -1580,7 +1552,7 @@ ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } -ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *engine) +ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *engine) { // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than // the jitted function, so it can safely do a tail call. @@ -1603,20 +1575,21 @@ ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *en } memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(engine, fo.function(), frame->jsFrame->args, argc, frame->callerCanHandleTailCall); + frame->init(engine, fo.function(), frame->jsFrame->argValues<Value>(), argc, + frame->callerCanHandleTailCall); frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue()); engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize(); frame->pendingTailCall = true; return Encode::undefined(); } -void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) +void Runtime::ThrowException::call(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) engine->throwError(value); } -ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &value) +ReturnedValue Runtime::TypeofValue::call(ExecutionEngine *engine, const Value &value) { Scope scope(engine); ScopedString res(scope); @@ -1647,83 +1620,110 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & return res.asReturnedValue(); } -QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; - return method_typeofValue(engine, prop); + return TypeofValue::call(engine, prop); } -ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame) +void Runtime::PushCallContext::call(CppStackFrame *frame) { - QV4::Value &accumulator = jsStackFrame[CallData::Accumulator]; - accumulator = accumulator.toObject(engine); - if (engine->hasException) - return Encode::undefined(); - Q_ASSERT(accumulator.isObject()); - const Object &obj = static_cast<const Object &>(accumulator); - ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context); - return context->newWithContext(obj.d())->asReturnedValue(); + frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue(); } -ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex) +ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc) { - ExecutionEngine *e = parent->engine(); - return parent->newCatchContext(e->currentStackFrame, blockIndex, - e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue(); + CallData *jsFrame = engine->currentStackFrame->jsFrame; + Value &newAcc = jsFrame->accumulator.asValue<Value>(); + newAcc = Value::fromHeapObject(acc.toObject(engine)); + if (!engine->hasException) { + Q_ASSERT(newAcc.isObject()); + const Object &obj = static_cast<const Object &>(newAcc); + Value &context = jsFrame->context.asValue<Value>(); + auto ec = static_cast<const ExecutionContext *>(&context); + context = ec->newWithContext(obj.d())->asReturnedValue(); + } + return newAcc.asReturnedValue(); } -ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index) +void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex) { - ExecutionEngine *e = parent->engine(); - return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue(); + auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex]; + engine->currentStackFrame->jsFrame->context = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue(); } -ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous) +void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index) { - return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue(); + engine->currentStackFrame->jsFrame->context = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); } +void Runtime::CloneBlockContext::call(ExecutionEngine *engine) +{ + auto frame = engine->currentStackFrame; + auto context = static_cast<Heap::CallContext *>( + Value::fromStaticValue(frame->jsFrame->context).m()); + frame->jsFrame->context = + ExecutionContext::cloneBlockContext(engine, context)->asReturnedValue(); +} -ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index) +void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index) { Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext || engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext); ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); engine->setScriptContext(c); - return c; + engine->currentStackFrame->jsFrame->context = c; } -ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine) +void Runtime::PopScriptContext::call(ExecutionEngine *engine) { ReturnedValue root = engine->rootContext()->asReturnedValue(); engine->setScriptContext(root); - return root; + engine->currentStackFrame->jsFrame->context = root; } -void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex) +void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); engine->throwReferenceError(name); } -void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) +void Runtime::ThrowOnNullOrUndefined::call(ExecutionEngine *engine, const Value &v) +{ + if (v.isNullOrUndefined()) + engine->throwTypeError(); +} + +ReturnedValue Runtime::ConvertThisToObject::call(ExecutionEngine *engine, const Value &t) +{ + if (!t.isObject()) { + if (t.isNullOrUndefined()) { + return engine->globalObject->asReturnedValue(); + } else { + return t.toObject(engine)->asReturnedValue(); + } + } + return t.asReturnedValue(); +} + +void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable); } -ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length) +ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length) { return engine->newArrayObject(values, length)->asReturnedValue(); } -ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, const QV4::Value *args, int argc) +ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId, QV4::Value args[], int argc) { Scope scope(engine); Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]); @@ -1755,7 +1755,8 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId if (arg != ObjectLiteralArgument::Value) { Q_ASSERT(args[2].isInteger()); int functionId = args[2].integerValue(); - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() + ->runtimeFunctions[functionId]; Q_ASSERT(clos); PropertyKey::FunctionNamePrefix prefix = PropertyKey::None; @@ -1796,9 +1797,11 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId return o.asReturnedValue(); } -ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames) +ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex, + const Value &superClass, Value computedNames[]) { - const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit; + const QV4::ExecutableCompilationUnit *unit + = engine->currentStackFrame->v4Function->executableCompilationUnit(); const QV4::CompiledData::Class *cls = unit->unitData()->classAt(classIndex); Scope scope(engine); @@ -1898,20 +1901,20 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde return constructor->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *engine) { Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject); return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine) { Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex) +QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex) { const Value *values = engine->currentStackFrame->originalArguments + argIndex; int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; @@ -1920,14 +1923,33 @@ QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, return engine->newArrayObject(values, nValues)->asReturnedValue(); } -ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) +ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id) { - Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); + const auto val + = engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id]; + Heap::RegExpObject *ro = engine->newRegExpObject(Value::fromStaticValue(val).as<RegExp>()); return ro->asReturnedValue(); } -#endif // V4_BOOTSTRAP -ReturnedValue Runtime::method_uMinus(const Value &value) +ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj) +{ + if (obj.isObject()) + return obj.asReturnedValue(); + + return obj.toObject(engine)->asReturnedValue(); +} + +Bool Runtime::ToBoolean::call(const Value &obj) +{ + return obj.toBoolean(); +} + +ReturnedValue Runtime::ToNumber::call(ExecutionEngine *, const Value &v) +{ + return Encode(v.toNumber()); +} + +ReturnedValue Runtime::UMinus::call(const Value &value) { TRACE1(value); @@ -1943,8 +1965,7 @@ ReturnedValue Runtime::method_uMinus(const Value &value) // binary operators -#ifndef V4_BOOTSTRAP -ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right) +ReturnedValue Runtime::Add::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); @@ -1956,7 +1977,7 @@ ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, co return RuntimeHelpers::addHelper(engine, left, right); } -ReturnedValue Runtime::method_sub(const Value &left, const Value &right) +ReturnedValue Runtime::Sub::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1969,7 +1990,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right) return Value::fromDouble(lval - rval).asReturnedValue(); } -ReturnedValue Runtime::method_mul(const Value &left, const Value &right) +ReturnedValue Runtime::Mul::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1982,7 +2003,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right) return Value::fromDouble(lval * rval).asReturnedValue(); } -ReturnedValue Runtime::method_div(const Value &left, const Value &right) +ReturnedValue Runtime::Div::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2003,7 +2024,7 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right) return Value::fromDouble(lval / rval).asReturnedValue(); } -ReturnedValue Runtime::method_mod(const Value &left, const Value &right) +ReturnedValue Runtime::Mod::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2024,7 +2045,43 @@ ReturnedValue Runtime::method_mod(const Value &left, const Value &right) return Value::fromDouble(std::fmod(lval, rval)).asReturnedValue(); } -ReturnedValue Runtime::method_shl(const Value &left, const Value &right) +ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp) +{ + double b = base.toNumber(); + double e = exp.toNumber(); + if (qt_is_inf(e) && (b == 1 || b == -1)) + return Encode(qt_qnan()); + return Encode(pow(b,e)); +} + +ReturnedValue Runtime::BitAnd::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval & rval)); +} + +ReturnedValue Runtime::BitOr::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval | rval)); +} + +ReturnedValue Runtime::BitXor::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval ^ rval)); +} + +ReturnedValue Runtime::Shl::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2033,7 +2090,7 @@ ReturnedValue Runtime::method_shl(const Value &left, const Value &right) return Encode((int)(lval << rval)); } -ReturnedValue Runtime::method_shr(const Value &left, const Value &right) +ReturnedValue Runtime::Shr::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2042,7 +2099,7 @@ ReturnedValue Runtime::method_shr(const Value &left, const Value &right) return Encode((int)(lval >> rval)); } -ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) +ReturnedValue Runtime::UShr::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2053,37 +2110,35 @@ ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) return Encode(res); } -#endif // V4_BOOTSTRAP - -ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right) +ReturnedValue Runtime::GreaterThan::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareGreaterThan(left, right); + bool r = CompareGreaterThan::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_lessThan(const Value &left, const Value &right) +ReturnedValue Runtime::LessThan::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareLessThan(left, right); + bool r = CompareLessThan::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_greaterEqual(const Value &left, const Value &right) +ReturnedValue Runtime::GreaterEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareGreaterEqual(left, right); + bool r = CompareGreaterEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right) +ReturnedValue Runtime::LessEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareLessEqual(left, right); + bool r = CompareLessEqual::call(left, right); return Encode(r); } @@ -2107,18 +2162,16 @@ struct LazyScope } }; -Bool Runtime::method_compareEqual(const Value &left, const Value &right) +Bool Runtime::CompareEqual::call(const Value &left, const Value &right) { TRACE2(left, right); Value lhs = left; Value rhs = right; -#ifndef V4_BOOTSTRAP LazyScope scope; Value *lhsGuard = nullptr; Value *rhsGuard = nullptr; -#endif redo: if (lhs.asReturnedValue() == rhs.asReturnedValue()) @@ -2148,7 +2201,6 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) case QV4::Value::QT_ManagedOrUndefined1: case QV4::Value::QT_ManagedOrUndefined2: case QV4::Value::QT_ManagedOrUndefined3: { -#ifndef V4_BOOTSTRAP // RHS: Managed Heap::Base *l = lhs.m(); Heap::Base *r = rhs.m(); @@ -2166,7 +2218,6 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) lhs = lhsGuard->asReturnedValue(); break; } -#endif return false; } case QV4::Value::QT_Empty: @@ -2178,16 +2229,12 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) rhs = Value::fromDouble(rhs.int_32()); // fall through default: // double -#ifndef V4_BOOTSTRAP if (lhs.m()->internalClass->vtable->isStringOrSymbol) { return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; } else { scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine); lhs = lhsGuard->asReturnedValue(); } -#else - Q_UNIMPLEMENTED(); -#endif } goto redo; case QV4::Value::QT_Empty: @@ -2216,23 +2263,23 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) } } -ReturnedValue Runtime::method_equal(const Value &left, const Value &right) +ReturnedValue Runtime::Equal::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareEqual(left, right); + bool r = CompareEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_notEqual(const Value &left, const Value &right) +ReturnedValue Runtime::NotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = !method_compareEqual(left, right); + bool r = !CompareEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) +ReturnedValue Runtime::StrictEqual::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2240,7 +2287,7 @@ ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) return Encode(r); } -ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &right) +ReturnedValue Runtime::StrictNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2248,21 +2295,21 @@ ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &rig return Encode(r); } -Bool Runtime::method_compareNotEqual(const Value &left, const Value &right) +Bool Runtime::CompareNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - return !Runtime::method_compareEqual(left, right); + return !Runtime::CompareEqual::call(left, right); } -Bool Runtime::method_compareStrictEqual(const Value &left, const Value &right) +Bool Runtime::CompareStrictEqual::call(const Value &left, const Value &right) { TRACE2(left, right); return RuntimeHelpers::strictEqual(left, right); } -Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right) +Bool Runtime::CompareStrictNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 2be3ebf012..cb89b7e6c0 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -52,8 +52,6 @@ #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4context_p.h" -#include "qv4engine_p.h" #include "qv4math_p.h" #include "qv4runtimeapi_p.h" #include <QtCore/qnumeric.h> @@ -114,20 +112,16 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static Bool strictEqual(const Value &x, const Value &y); static ReturnedValue addHelper(ExecutionEngine *engine, const Value &left, const Value &right); - - static ReturnedValue getTemplateObject(Function *function, int index); }; // type conversion and testing -#ifndef V4_BOOTSTRAP inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint) { if (!value.isObject()) return value.asReturnedValue(); return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint); } -#endif inline double RuntimeHelpers::toNumber(const Value &value) { diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index ceec13a3bb..05ffb84d58 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -51,179 +51,449 @@ // #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE namespace QV4 { typedef uint Bool; -struct NoThrowEngine; -namespace { -template <typename T> -struct ExceptionCheck { - enum { NeedsCheck = 1 }; -}; -// push_catch and pop context methods shouldn't check for exceptions -template <> -struct ExceptionCheck<void (*)(QV4::NoThrowEngine *)> { - enum { NeedsCheck = 0 }; -}; -template <typename A> -struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> { - enum { NeedsCheck = 0 }; -}; -template <> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> { - enum { NeedsCheck = 0 }; -}; -template <typename A> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> { - enum { NeedsCheck = 0 }; -}; -template <typename A, typename B> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> { - enum { NeedsCheck = 0 }; -}; -template <typename A, typename B, typename C> -struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { - enum { NeedsCheck = 0 }; -}; -} // anonymous namespace - -#define FOR_EACH_RUNTIME_METHOD(F) \ - /* call */ \ - F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ - F(ReturnedValue, callQmlContextPropertyLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ - F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \ - F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \ - F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \ - F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \ - F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ - F(ReturnedValue, callWithReceiver, (ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)) \ - F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \ - F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \ - F(ReturnedValue, tailCall, (CppStackFrame *frame, ExecutionEngine *engine)) \ - \ - /* construct */ \ - F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ - F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ - \ - /* load & store */ \ - F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ - F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ - F(void, storeElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)) \ - F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ - F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ - F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ - F(ReturnedValue, loadElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)) \ - F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \ - F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \ - F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \ - \ - /* typeof */ \ - F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ - F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ - \ - /* delete */ \ - F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \ - F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \ - \ - /* exceptions & scopes */ \ - F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ - F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \ - F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \ - F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \ - F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \ - F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \ - F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \ - F(void, throwReferenceError, (ExecutionEngine *engine, int nameIndex)) \ - \ - /* closures */ \ - F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ - \ - /* function header */ \ - F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ - F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \ - F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \ - F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \ - \ - /* literals */ \ - F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ - F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, const Value *args, int argc)) \ - F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \ - \ - /* for-in, for-of and array destructuring */ \ - F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ - F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \ - F(ReturnedValue, iteratorNextForYieldStar, (ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)) \ - F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \ - F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \ - \ - /* unary operators */ \ - F(ReturnedValue, uMinus, (const Value &value)) \ - \ - /* binary operators */ \ - F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, sub, (const Value &left, const Value &right)) \ - F(ReturnedValue, mul, (const Value &left, const Value &right)) \ - F(ReturnedValue, div, (const Value &left, const Value &right)) \ - F(ReturnedValue, mod, (const Value &left, const Value &right)) \ - F(ReturnedValue, shl, (const Value &left, const Value &right)) \ - F(ReturnedValue, shr, (const Value &left, const Value &right)) \ - F(ReturnedValue, ushr, (const Value &left, const Value &right)) \ - F(ReturnedValue, greaterThan, (const Value &left, const Value &right)) \ - F(ReturnedValue, lessThan, (const Value &left, const Value &right)) \ - F(ReturnedValue, greaterEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, lessEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, equal, (const Value &left, const Value &right)) \ - F(ReturnedValue, notEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, strictEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, strictNotEqual, (const Value &left, const Value &right)) \ - \ - /* comparisons */ \ - F(Bool, compareGreaterThan, (const Value &l, const Value &r)) \ - F(Bool, compareLessThan, (const Value &l, const Value &r)) \ - F(Bool, compareGreaterEqual, (const Value &l, const Value &r)) \ - F(Bool, compareLessEqual, (const Value &l, const Value &r)) \ - F(Bool, compareEqual, (const Value &left, const Value &right)) \ - F(Bool, compareNotEqual, (const Value &left, const Value &right)) \ - F(Bool, compareStrictEqual, (const Value &left, const Value &right)) \ - F(Bool, compareStrictNotEqual, (const Value &left, const Value &right)) \ - \ - F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - \ - F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) struct Q_QML_PRIVATE_EXPORT Runtime { - Runtime(); - typedef ReturnedValue (*UnaryOperation)(const Value &value); typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); - typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right); + typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *, const Value &left, const Value &right); -#define DEFINE_RUNTIME_METHOD_ENUM(returnvalue, name, args) name, - enum RuntimeMethods { - FOR_EACH_RUNTIME_METHOD(DEFINE_RUNTIME_METHOD_ENUM) - RuntimeMethodCount, - InvalidRuntimeMethod = RuntimeMethodCount + enum class Throws { No, Yes }; + enum class ChangesContext { No, Yes }; + enum class Pure { No, Yes }; + enum class LastArgumentIsOutputValue { No, Yes }; + + template<Throws t, ChangesContext c = ChangesContext::No, Pure p = Pure::No, + LastArgumentIsOutputValue out = LastArgumentIsOutputValue::No> + struct Method + { + static constexpr bool throws = t == Throws::Yes; + static constexpr bool changesContext = c == ChangesContext::Yes; + static constexpr bool pure = p == Pure::Yes; + static constexpr bool lastArgumentIsOutputValue = out == LastArgumentIsOutputValue::Yes; + }; + using PureMethod = Method<Throws::No, ChangesContext::No, Pure::Yes>; + using IteratorMethod = Method<Throws::Yes, ChangesContext::No, Pure::No, + LastArgumentIsOutputValue::Yes>; + + /* call */ + struct Q_QML_PRIVATE_EXPORT CallGlobalLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, uint, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallElement : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallValue : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallWithReceiver : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallPossiblyDirectEval : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallWithSpread : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT TailCall : Method<Throws::Yes> + { + static ReturnedValue call(CppStackFrame *, ExecutionEngine *); }; -#undef DEFINE_RUNTIME_METHOD_ENUM - void *runtimeMethods[RuntimeMethodCount]; + /* construct */ + struct Q_QML_PRIVATE_EXPORT Construct : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT ConstructWithSpread : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; - static uint runtimeMethodOffset(RuntimeMethods method) { return method*QT_POINTER_SIZE; } + /* load & store */ + struct Q_QML_PRIVATE_EXPORT StoreNameStrict : Method<Throws::Yes> + { + static void call(ExecutionEngine *, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreNameSloppy : Method<Throws::Yes> + { + static void call(ExecutionEngine *, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreProperty : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreElement : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, int); + }; + struct Q_QML_PRIVATE_EXPORT LoadName : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT LoadElement : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreSuperProperty : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadSuperConstructor : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadGlobalLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, int); + }; + struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint); + }; + struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int); + }; + struct Q_QML_PRIVATE_EXPORT SetLookupStrict : Method<Throws::Yes> + { + static void call(Function *, const Value &, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT SetLookupSloppy : Method<Throws::Yes> + { + static void call(Function *, const Value &, int, const Value &); + }; + + /* typeof */ + struct Q_QML_PRIVATE_EXPORT TypeofValue : PureMethod + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT TypeofName : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *, int); + }; + + /* delete */ + struct Q_QML_PRIVATE_EXPORT DeleteProperty_NoThrow : Method<Throws::No> + { + static Bool call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DeleteProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DeleteName_NoThrow : Method<Throws::No> + { + static Bool call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT DeleteName : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, int); + }; + + /* exceptions & scopes */ + struct Q_QML_PRIVATE_EXPORT ThrowException : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(CppStackFrame *); + }; + struct Q_QML_PRIVATE_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *, int, int); + }; + struct Q_QML_PRIVATE_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT ThrowReferenceError : Method<Throws::Yes> + { + static void call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &); + }; + + /* closures */ + struct Q_QML_PRIVATE_EXPORT Closure : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *, int); + }; -#define RUNTIME_METHOD(returnvalue, name, args) \ - typedef returnvalue (*Method_##name)args; \ - enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \ - static returnvalue method_##name args; - FOR_EACH_RUNTIME_METHOD(RUNTIME_METHOD) -#undef RUNTIME_METHOD + /* Function header */ + struct Q_QML_PRIVATE_EXPORT ConvertThisToObject : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DeclareVar : Method<Throws::Yes> + { + static void call(ExecutionEngine *, Bool, int); + }; + struct Q_QML_PRIVATE_EXPORT CreateMappedArgumentsObject : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT CreateRestParameter : PureMethod + { + static ReturnedValue call(ExecutionEngine *, int); + }; + + /* literals */ + struct Q_QML_PRIVATE_EXPORT ArrayLiteral : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Value[], uint); + }; + struct Q_QML_PRIVATE_EXPORT ObjectLiteral : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CreateClass : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int, const Value &, Value[]); + }; + + /* for-in, for-of and array destructuring */ + struct Q_QML_PRIVATE_EXPORT GetIterator : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, int); + }; + struct Q_QML_PRIVATE_EXPORT IteratorNext : IteratorMethod + { + static ReturnedValue call(ExecutionEngine *, const Value &, Value *); + }; + struct Q_QML_PRIVATE_EXPORT IteratorNextForYieldStar : IteratorMethod + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value *); + }; + struct Q_QML_PRIVATE_EXPORT IteratorClose : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DestructureRestElement : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + + /* conversions */ + struct Q_QML_PRIVATE_EXPORT ToObject : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT ToBoolean : PureMethod + { + static Bool call(const Value &); + }; + struct Q_QML_PRIVATE_EXPORT ToNumber : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + /* unary operators */ + struct Q_QML_PRIVATE_EXPORT UMinus : Method<Throws::Yes> + { + static ReturnedValue call(const Value &); + }; + + /* binary operators */ + struct Q_QML_PRIVATE_EXPORT Instanceof : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT In : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Add : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Sub : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Mul : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Div : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Mod : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Exp : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT BitAnd : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT BitOr : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT BitXor : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Shl : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Shr : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT UShr : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT GreaterThan : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LessThan : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT GreaterEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LessEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Equal : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT NotEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StrictEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StrictNotEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + + /* comparisons */ + struct Q_QML_PRIVATE_EXPORT CompareGreaterThan : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareLessThan : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareGreaterEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareLessEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareNotEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareStrictEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareStrictNotEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + + struct Q_QML_PRIVATE_EXPORT CompareInstanceof : Method<Throws::Yes> + { + static Bool call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareIn : Method<Throws::Yes> + { + static Bool call(ExecutionEngine *, const Value &, const Value &); + }; + + struct Q_QML_PRIVATE_EXPORT RegexpLiteral : PureMethod + { + static ReturnedValue call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT GetTemplateObject : PureMethod + { + static ReturnedValue call(Function *, int); + }; struct StackOffsets { static const int tailCall_function = -1; @@ -234,7 +504,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { }; static_assert(std::is_standard_layout<Runtime>::value, "Runtime needs to be standard layout in order for us to be able to use offsetof"); -static_assert(offsetof(Runtime, runtimeMethods) == 0, "JIT expects this to be the first member"); static_assert(sizeof(Runtime::BinaryOperation) == sizeof(void*), "JIT expects a function pointer to fit into a regular pointer, for cross-compilation offset translation"); } // namespace QV4 diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index 9866966936..162d75db63 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -37,10 +37,12 @@ ** ****************************************************************************/ +#include "qv4engine_p.h" #include "qv4runtimecodegen_p.h" -#include "qv4compilerscanfunctions_p.h" +#include <private/qv4compilerscanfunctions_p.h> using namespace QV4; +using namespace QQmlJS; void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, @@ -58,7 +60,7 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, scan(ast); scan.leaveEnvironment(); - if (hasError) + if (hasError()) return; int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); @@ -67,17 +69,19 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) { - if (hasError) + if (hasError()) return; - hasError = true; + + Codegen::throwSyntaxError(loc, detail); engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); } void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) { - if (hasError) + if (hasError()) return; - hasError = true; + + Codegen::throwReferenceError(loc, detail); engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); } diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h index 006a6a3cde..71aaf1fb55 100644 --- a/src/qml/jsruntime/qv4runtimecodegen_p.h +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -66,11 +66,11 @@ public: void generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, - AST::FunctionExpression *ast, + QQmlJS::AST::FunctionExpression *ast, Compiler::Module *module); - void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; - void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; + void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; + void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; private: ExecutionEngine *engine; diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index e4aceef3ee..12a6381e6f 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -70,7 +70,7 @@ struct ScopedValue; #define CHECK_EXCEPTION() \ do { \ - if (scope.hasException()) { \ + if (scope.hasException() || scope.engine->isInterrupted.loadAcquire()) { \ return QV4::Encode::undefined(); \ } \ } while (false) diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 6cb2e95cdc..2fab9e4b7b 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -60,8 +60,9 @@ #include <QScopedValueRollback> using namespace QV4; +using namespace QQmlJS; -Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit) +Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit) : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) , compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true) { @@ -108,10 +109,10 @@ void Script::parse() const auto diagnosticMessages = parser.diagnosticMessages(); for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isError()) { - valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn); + valueScope.engine->throwSyntaxError(m.message, sourceFile, m.line, m.column); return; } else { - qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn + qWarning() << sourceFile << ':' << m.line << ':' << m.column << ": warning: " << m.message; } } @@ -133,7 +134,7 @@ void Script::parse() if (v4->hasException) return; - compilationUnit = cg.generateCompilationUnit(); + compilationUnit = QV4::ExecutableCompilationUnit::create(cg.generateCompilationUnit()); vmFunction = compilationUnit->linkToEngine(v4); } @@ -172,10 +173,11 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, - const QString &fileName, const QString &finalUrl, const QString &source, - QList<QQmlError> *reportedErrors, - QV4::Compiler::ContextType contextType) +QV4::CompiledData::CompilationUnit Script::precompile( + QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, + Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, + const QString &source, QList<QQmlError> *reportedErrors, + QV4::Compiler::ContextType contextType) { using namespace QV4::Compiler; using namespace QQmlJS::AST; @@ -202,10 +204,16 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi Codegen cg(unitGenerator, /*strict mode*/false); cg.generateFromProgram(fileName, finalUrl, source, program, module, contextType); - errors = cg.qmlErrors(); - if (!errors.isEmpty()) { - if (reportedErrors) - *reportedErrors << errors; + if (cg.hasError()) { + if (reportedErrors) { + const auto v4Error = cg.error(); + QQmlError error; + error.setUrl(cg.url()); + error.setLine(v4Error.line); + error.setColumn(v4Error.column); + error.setDescription(v4Error.message); + reportedErrors->append(error); + } return nullptr; } @@ -219,8 +227,9 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(originalUrl, &cacheError)) { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> jsUnit; - jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit)); + QQmlRefPointer<QV4::ExecutableCompilationUnit> jsUnit + = QV4::ExecutableCompilationUnit::create( + QV4::CompiledData::CompilationUnit(cachedUnit)); return new QV4::Script(engine, qmlContext, jsUnit); } @@ -237,7 +246,6 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo QByteArray data = f.readAll(); QString sourceCode = QString::fromUtf8(data); - QmlIR::Document::removeScriptPragmas(sourceCode); auto result = new QV4::Script(engine, qmlContext, /*parseAsBinding*/false, sourceCode, originalUrl.toString()); result->contextType = QV4::Compiler::ContextType::ScriptImportedByQML; diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index a1e9b83a8b..aecedea701 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -80,7 +80,7 @@ struct Q_QML_EXPORT Script { if (qml) qmlContext.set(engine, *qml); } - Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit); + Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit); ~Script(); QString sourceFile; int line; @@ -92,7 +92,7 @@ struct Q_QML_EXPORT Script { bool parsed; QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval; QV4::PersistentValue qmlContext; - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<ExecutableCompilationUnit> compilationUnit; Function *vmFunction; bool parseAsBinding; @@ -101,8 +101,10 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, - const QString &fileName, const QString &finalUrl, const QString &source, + static QV4::CompiledData::CompilationUnit precompile( + QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, + Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, + const QString &finalUrl, const QString &source, QList<QQmlError> *reportedErrors = nullptr, QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Global); static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 1eef12a491..77a98247ac 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -49,8 +49,10 @@ #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" #include <private/qqmlvaluetypewrapper_p.h> +#if QT_CONFIG(qml_itemmodel) #include <private/qqmlmodelindexvaluetype_p.h> #include <QtCore/qabstractitemmodel.h> +#endif #include <algorithm> @@ -75,6 +77,16 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description } // F(elementType, elementTypeName, sequenceType, defaultValue) +#if QT_CONFIG(qml_itemmodel) +#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) \ + F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ + F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ + F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ + F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) +#else +#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) +#endif + #define FOREACH_QML_SEQUENCE_TYPE(F) \ F(int, IntVector, QVector<int>, 0) \ F(qreal, RealVector, QVector<qreal>, 0.0) \ @@ -92,10 +104,7 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description F(QUrl, Url, QList<QUrl>, QUrl()) \ F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \ F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ - F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ - F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ - F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ - F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) + FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) { @@ -112,6 +121,7 @@ static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, co return engine->newString(element.toString())->asReturnedValue(); } +#if QT_CONFIG(qml_itemmodel) static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QModelIndex &element) { const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(QMetaType::QModelIndex); @@ -124,6 +134,7 @@ static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, co const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(metaTypeId); return QV4::QQmlValueTypeWrapper::create(engine, QVariant::fromValue(element), vtmo, metaTypeId); } +#endif static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, qreal element) { @@ -150,6 +161,7 @@ static QString convertElementToString(const QUrl &element) return element.toString(); } +#if QT_CONFIG(qml_itemmodel) static QString convertElementToString(const QModelIndex &element) { return reinterpret_cast<const QQmlModelIndexValueType *>(&element)->toString(); @@ -159,6 +171,7 @@ static QString convertElementToString(const QItemSelectionRange &element) { return reinterpret_cast<const QQmlItemSelectionRangeValueType *>(&element)->toString(); } +#endif static QString convertElementToString(qreal element) { @@ -192,6 +205,7 @@ template <> QUrl convertValueToElement(const Value &value) return QUrl(value.toQString()); } +#if QT_CONFIG(qml_itemmodel) template <> QModelIndex convertValueToElement(const Value &value) { const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); @@ -207,6 +221,7 @@ template <> QItemSelectionRange convertValueToElement(const Value &value) return v->toVariant().value<QItemSelectionRange>(); return QItemSelectionRange(); } +#endif template <> qreal convertValueToElement(const Value &value) { @@ -667,6 +682,7 @@ typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList); typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList); +#if QT_CONFIG(qml_itemmodel) typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList); typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList; @@ -675,6 +691,7 @@ typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList); typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList); +#endif typedef QQmlSequence<QList<bool> > QQmlBoolList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolList); typedef QQmlSequence<QList<qreal> > QQmlRealList; diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index da71215bed..886dfaa521 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct SequencePrototype : public QV4::Object +struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object { V4_PROTOTYPE(arrayPrototype) void init(); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp deleted file mode 100644 index 50871a4d87..0000000000 --- a/src/qml/jsruntime/qv4serialize.cpp +++ /dev/null @@ -1,427 +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$ -** -****************************************************************************/ - -#include "qv4serialize_p.h" - -#include <private/qv8engine_p.h> -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#include <private/qqmllistmodelworkeragent_p.h> -#endif - -#include <private/qv4value_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4regexpobject_p.h> -#if QT_CONFIG(qml_sequence_object) -#include <private/qv4sequenceobject_p.h> -#endif -#include <private/qv4objectproto_p.h> -#include <private/qv4qobjectwrapper_p.h> - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -// We allow the following JavaScript types to be passed between the main and -// the secondary thread: -// + undefined -// + null -// + Boolean -// + String -// + Function -// + Array -// + "Simple" Objects -// + Number -// + Date -// + RegExp -// <quint8 type><quint24 size><data> - -enum Type { - WorkerUndefined, - WorkerNull, - WorkerTrue, - WorkerFalse, - WorkerString, - WorkerFunction, - WorkerArray, - WorkerObject, - WorkerInt32, - WorkerUint32, - WorkerNumber, - WorkerDate, - WorkerRegexp, -#if QT_CONFIG(qml_list_model) - WorkerListModel, -#endif -#if QT_CONFIG(qml_sequence_object) - WorkerSequence -#endif -}; - -static inline quint32 valueheader(Type type, quint32 size = 0) -{ - return quint8(type) << 24 | (size & 0xFFFFFF); -} - -static inline Type headertype(quint32 header) -{ - return (Type)(header >> 24); -} - -static inline quint32 headersize(quint32 header) -{ - return header & 0xFFFFFF; -} - -static inline void push(QByteArray &data, quint32 value) -{ - data.append((const char *)&value, sizeof(quint32)); -} - -static inline void push(QByteArray &data, double value) -{ - data.append((const char *)&value, sizeof(double)); -} - -static inline void push(QByteArray &data, void *ptr) -{ - data.append((const char *)&ptr, sizeof(void *)); -} - -static inline void reserve(QByteArray &data, int extra) -{ - data.reserve(data.size() + extra); -} - -static inline quint32 popUint32(const char *&data) -{ - quint32 rv = *((const quint32 *)data); - data += sizeof(quint32); - return rv; -} - -static inline double popDouble(const char *&data) -{ - double rv = *((const double *)data); - data += sizeof(double); - return rv; -} - -static inline void *popPtr(const char *&data) -{ - void *rv = *((void *const *)data); - data += sizeof(void *); - return rv; -} - -// XXX TODO: Check that worker script is exception safe in the case of -// serialization/deserialization failures - -#define ALIGN(size) (((size) + 3) & ~3) -void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine) -{ - QV4::Scope scope(engine); - - if (v.isEmpty()) { - Q_ASSERT(!"Serialize: got empty value"); - } else if (v.isUndefined()) { - push(data, valueheader(WorkerUndefined)); - } else if (v.isNull()) { - push(data, valueheader(WorkerNull)); - } else if (v.isBoolean()) { - push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); - } else if (v.isString()) { - const QString &qstr = v.toQString(); - int length = qstr.length(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, utf16size + sizeof(quint32)); - push(data, valueheader(WorkerString, length)); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, qstr.constData(), length*sizeof(QChar)); - } else if (v.as<FunctionObject>()) { - // XXX TODO: Implement passing function objects between the main and - // worker scripts - push(data, valueheader(WorkerUndefined)); - } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) { - uint length = array->getLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerArray, length)); - ScopedValue val(scope); - for (uint ii = 0; ii < length; ++ii) - serialize(data, (val = array->get(ii)), engine); - } else if (v.isInteger()) { - reserve(data, 2 * sizeof(quint32)); - push(data, valueheader(WorkerInt32)); - push(data, (quint32)v.integerValue()); -// } else if (v.IsUint32()) { -// reserve(data, 2 * sizeof(quint32)); -// push(data, valueheader(WorkerUint32)); -// push(data, v.Uint32Value()); - } else if (v.isNumber()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerNumber)); - push(data, v.asDouble()); - } else if (const QV4::DateObject *d = v.as<DateObject>()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerDate)); - push(data, d->date()); - } else if (const RegExpObject *re = v.as<RegExpObject>()) { - quint32 flags = re->flags(); - QString pattern = re->source(); - int length = pattern.length() + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, sizeof(quint32) + utf16size); - push(data, valueheader(WorkerRegexp, flags)); - push(data, (quint32)length); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, pattern.constData(), length*sizeof(QChar)); - } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { - // XXX TODO: Generalize passing objects between the main thread and worker scripts so - // that others can trivially plug in their elements. -#if QT_CONFIG(qml_list_model) - QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); - if (lm && lm->agent()) { - QQmlListModelWorkerAgent *agent = lm->agent(); - agent->addref(); - push(data, valueheader(WorkerListModel)); - push(data, (void *)agent); - return; - } -#else - Q_UNUSED(qobjectWrapper); -#endif - // No other QObject's are allowed to be sent - push(data, valueheader(WorkerUndefined)); - } else if (const Object *o = v.as<Object>()) { -#if QT_CONFIG(qml_sequence_object) - if (o->isListType()) { - // valid sequence. we generate a length (sequence length + 1 for the sequence type) - uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); - uint length = seqLength + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerSequence, length)); - serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type - ScopedValue val(scope); - for (uint ii = 0; ii < seqLength; ++ii) - serialize(data, (val = o->get(ii)), engine); // sequence elements - - return; - } -#endif - - // regular object - QV4::ScopedValue val(scope, v); - QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val)); - quint32 length = properties->getLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - push(data, valueheader(WorkerObject, length)); - - QV4::ScopedValue s(scope); - for (quint32 ii = 0; ii < length; ++ii) { - s = properties->get(ii); - serialize(data, s, engine); - - QV4::String *str = s->as<String>(); - val = o->get(str); - if (scope.hasException()) - scope.engine->catchException(); - - serialize(data, val, engine); - } - return; - } else { - push(data, valueheader(WorkerUndefined)); - } -} - -ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) -{ - quint32 header = popUint32(data); - Type type = headertype(header); - - Scope scope(engine); - - switch (type) { - case WorkerUndefined: - return QV4::Encode::undefined(); - case WorkerNull: - return QV4::Encode::null(); - case WorkerTrue: - return QV4::Encode(true); - case WorkerFalse: - return QV4::Encode(false); - case WorkerString: - { - quint32 size = headersize(header); - QString qstr((const QChar *)data, size); - data += ALIGN(size * sizeof(quint16)); - return QV4::Encode(engine->newString(qstr)); - } - case WorkerFunction: - Q_ASSERT(!"Unreachable"); - break; - case WorkerArray: - { - quint32 size = headersize(header); - ScopedArrayObject a(scope, engine->newArrayObject()); - ScopedValue v(scope); - for (quint32 ii = 0; ii < size; ++ii) { - v = deserialize(data, engine); - a->put(ii, v); - } - return a.asReturnedValue(); - } - case WorkerObject: - { - quint32 size = headersize(header); - ScopedObject o(scope, engine->newObject()); - ScopedValue name(scope); - ScopedString n(scope); - ScopedValue value(scope); - for (quint32 ii = 0; ii < size; ++ii) { - name = deserialize(data, engine); - value = deserialize(data, engine); - n = name->asReturnedValue(); - o->put(n, value); - } - return o.asReturnedValue(); - } - case WorkerInt32: - return QV4::Encode((qint32)popUint32(data)); - case WorkerUint32: - return QV4::Encode(popUint32(data)); - case WorkerNumber: - return QV4::Encode(popDouble(data)); - case WorkerDate: - return QV4::Encode(engine->newDateObject(QV4::Value::fromDouble(popDouble(data)))); - case WorkerRegexp: - { - quint32 flags = headersize(header); - quint32 length = popUint32(data); - QString pattern = QString((const QChar *)data, length - 1); - data += ALIGN(length * sizeof(quint16)); - return Encode(engine->newRegExpObject(pattern, flags)); - } -#if QT_CONFIG(qml_list_model) - case WorkerListModel: - { - void *ptr = popPtr(data); - QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; - QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent)); - // ### Find a better solution then the ugly property - QQmlListModelWorkerAgent::VariantRef ref(agent); - QVariant var = qVariantFromValue(ref); - QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); - QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); - rv->as<Object>()->defineReadonlyProperty(s, v); - - agent->release(); - agent->setEngine(engine); - return rv->asReturnedValue(); - } -#endif -#if QT_CONFIG(qml_sequence_object) - case WorkerSequence: - { - ScopedValue value(scope); - bool succeeded = false; - quint32 length = headersize(header); - quint32 seqLength = length - 1; - value = deserialize(data, engine); - int sequenceType = value->integerValue(); - ScopedArrayObject array(scope, engine->newArrayObject()); - array->arrayReserve(seqLength); - for (quint32 ii = 0; ii < seqLength; ++ii) { - value = deserialize(data, engine); - array->arrayPut(ii, value); - } - array->setArrayLengthUnchecked(seqLength); - QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); - return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); - } -#endif - } - Q_ASSERT(!"Unreachable"); - return QV4::Encode::undefined(); -} - -QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine) -{ - QByteArray rv; - serialize(rv, value, engine); - return rv; -} - -ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine) -{ - const char *stream = data.constData(); - return deserialize(stream, engine); -} - -QT_END_NAMESPACE - diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp index 088ecbe30d..1664d1bd71 100644 --- a/src/qml/jsruntime/qv4setobject.cpp +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -76,7 +76,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add"))))); if (!adder) return scope.engine->throwTypeError(); - ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true)); CHECK_EXCEPTION(); if (!iter) return a.asReturnedValue(); @@ -84,7 +84,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, Value *nextValue = scope.alloc(1); ScopedValue done(scope); forever { - done = Runtime::method_iteratorNext(scope.engine, iter, nextValue); + done = Runtime::IteratorNext::call(scope.engine, iter, nextValue); CHECK_EXCEPTION(); if (done->toBoolean()) return a.asReturnedValue(); @@ -92,7 +92,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, adder->call(a, nextValue, 1); if (scope.engine->hasException) { ScopedValue falsey(scope, Encode(false)); - return Runtime::method_iteratorClose(scope.engine, iter, falsey); + return Runtime::IteratorClose::call(scope.engine, iter, falsey); } } } diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index 44cfef9173..616fa9a5a9 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -52,63 +52,13 @@ #include <private/qv4context_p.h> #include <private/qv4enginebase_p.h> -#ifndef V4_BOOTSTRAP +#include <private/qv4calldata_p.h> #include <private/qv4function_p.h> -#endif 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 - }; - - Value function; - Value context; - Value accumulator; - Value thisObject; - Value newTarget; - Value _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() : Value::undefinedValue().asReturnedValue(); - } - - Value args[1]; - - static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value)); - struct Q_QML_EXPORT CppStackFrame { EngineBase *engine; Value *savedStackTop; @@ -155,7 +105,6 @@ struct Q_QML_EXPORT CppStackFrame { engine->jsStackTop = savedStackTop; } -#ifndef V4_BOOTSTRAP static uint requiredJSStackFrameSize(uint nRegisters) { return CallData::HeaderSize() + nRegisters; } @@ -168,7 +117,7 @@ struct Q_QML_EXPORT CppStackFrame { void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, const Value &thisObject, const Value &newTarget = Value::undefinedValue()) { setupJSFrame(stackSpace, function, scope, thisObject, newTarget, - v4Function->nFormals, v4Function->compiledFunction->nRegisters); + v4Function->compiledFunction->nFormals, v4Function->compiledFunction->nRegisters); } void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) @@ -198,7 +147,6 @@ struct Q_QML_EXPORT CppStackFrame { *v = Value::emptyValue().asReturnedValue(); } } -#endif QString source() const; QString function() const; diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 68d65f2e24..223f4a4769 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -39,19 +39,15 @@ #include "qv4string_p.h" #include "qv4value_p.h" -#ifndef V4_BOOTSTRAP #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" -#endif #include <QtCore/QHash> #include <QtCore/private/qnumeric_p.h> using namespace QV4; -#ifndef V4_BOOTSTRAP - void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack) { StringOrSymbol *s = static_cast<StringOrSymbol *>(that); @@ -254,10 +250,3 @@ qint64 String::virtualGetLength(const Managed *m) { return static_cast<const String *>(m)->d()->length(); } - -#endif // V4_BOOTSTRAP - -uint String::toArrayIndex(const QString &str) -{ - return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); -} diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index fbd4f5f550..52fe09cd72 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -54,6 +54,7 @@ #include "qv4managed_p.h" #include <QtCore/private/qnumeric_p.h> #include "qv4enginebase_p.h" +#include <private/qv4stringtoarrayindex_p.h> QT_BEGIN_NAMESPACE @@ -104,7 +105,6 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { static void markObjects(Heap::Base *that, MarkStack *markStack); -#ifndef V4_BOOTSTRAP const VTable *vtable() const { return internalClass->vtable; } @@ -140,11 +140,9 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { private: static void append(const String *data, QChar *ch); -#endif }; Q_STATIC_ASSERT(std::is_trivial< String >::value); -#ifndef V4_BOOTSTRAP struct ComplexString : String { void init(String *l, String *n); void init(String *ref, int from, int len); @@ -162,12 +160,10 @@ inline int String::length() const { return text ? text->size : static_cast<const ComplexString *>(this)->len; } -#endif } struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed { -#ifndef V4_BOOTSTRAP V4_MANAGED(StringOrSymbol, Managed) V4_NEEDS_DESTROY enum { @@ -184,11 +180,9 @@ public: inline QString toQString() const { return d()->toQString(); } -#endif }; struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { -#ifndef V4_BOOTSTRAP V4_MANAGED(String, StringOrSymbol) Q_MANAGED_TYPE(String) V4_INTERNALCLASS(String) @@ -239,43 +233,13 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static qint64 virtualGetLength(const Managed *m); -#endif - -public: - static uint toArrayIndex(const QString &str); - -private: - static inline uint toUInt(const QChar *ch) { return ch->unicode(); } - static inline uint toUInt(const char *ch) { return static_cast<unsigned char>(*ch); } - - template <typename T> - static inline uint toArrayIndex(const T *ch, const T *end) - { - uint i = toUInt(ch) - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = toUInt(ch) - '0'; - if (x > 9) - return UINT_MAX; - if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x - return UINT_MAX; - ++ch; - } - return i; - } public: template <typename T> static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) { // array indices get their number as hash value - uint h = toArrayIndex(ch, end); + uint h = stringToArrayIndex(ch, end); if (h != UINT_MAX) { if (subtype) *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; @@ -283,17 +247,16 @@ public: } while (ch < end) { - h = 31 * h + toUInt(ch); + h = 31 * h + charToUInt(ch); ++ch; } if (subtype) - *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; + *subtype = (charToUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; -#ifndef V4_BOOTSTRAP struct ComplexString : String { typedef QV4::Heap::ComplexString Data; QV4::Heap::ComplexString *d_unchecked() const { return static_cast<QV4::Heap::ComplexString *>(m()); } @@ -332,8 +295,6 @@ inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) { return v.toString(e)->asReturnedValue(); } -#endif - } diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 6afa6d36d6..9b4a2d575e 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -45,7 +45,7 @@ #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4symbol_p.h" -#include "qv4alloca_p.h" +#include <private/qv4alloca_p.h> #include "qv4jscall_p.h" #include "qv4stringiterator_p.h" #include <QtCore/QDateTime> @@ -152,13 +152,14 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert if (attributes != Attr_Invalid) return attributes; - const StringObject *s = static_cast<const StringObject *>(m); - uint slen = s->d()->string->toQString().length(); - uint index = id.asArrayIndex(); - if (index < slen) { - if (p) - p->value = s->getIndex(index); - return Attr_NotConfigurable|Attr_NotWritable; + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + const auto s = static_cast<const StringObject *>(m); + if (index < uint(s->d()->string->toQString().length())) { + if (p) + p->value = s->getIndex(index); + return Attr_NotConfigurable|Attr_NotWritable; + } } return Object::virtualGetOwnProperty(m, id, p); } diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index faf7934c06..7d33167762 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -214,7 +214,7 @@ template <typename T> ReturnedValue atomicLoad(char *data) { typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data); - T val = QAtomicOps<T>::load(*mem); + T val = QAtomicOps<T>::loadRelaxed(*mem); return typeToValue(val); } @@ -223,7 +223,7 @@ ReturnedValue atomicStore(char *data, Value v) { T value = valueToType<T>(v); typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data); - QAtomicOps<T>::store(*mem, value); + QAtomicOps<T>::storeRelaxed(*mem, value); return typeToValue(value); } @@ -459,24 +459,23 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + const bool isArrayIndex = id.isArrayIndex(); + if (!isArrayIndex && !id.isCanonicalNumericIndexString()) return Object::virtualGet(m, id, receiver, hasProperty); - // fall through, with index == UINT_MAX it'll do the right thing. Scope scope(static_cast<const Object *>(m)->engine()); Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); if (a->d()->buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); - if (index >= a->length()) { + if (!isArrayIndex || id.asArrayIndex() >= a->length()) { if (hasProperty) *hasProperty = false; return Encode::undefined(); } uint bytesPerElement = a->d()->type->bytesPerElement; - uint byteOffset = a->d()->byteOffset + index * bytesPerElement; + uint byteOffset = a->d()->byteOffset + id.asArrayIndex() * bytesPerElement; Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength()); if (hasProperty) @@ -486,27 +485,22 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + const bool isArrayIndex = id.isArrayIndex(); + if (!isArrayIndex && !id.isCanonicalNumericIndexString()) return Object::virtualHasProperty(m, id); - // fall through, with index == UINT_MAX it'll do the right thing. const TypedArray *a = static_cast<const TypedArray *>(m); if (a->d()->buffer->isDetachedBuffer()) { a->engine()->throwTypeError(); return false; } - if (index >= a->length()) - return false; - return true; + return isArrayIndex && id.asArrayIndex() < a->length(); } PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString()) return Object::virtualGetOwnProperty(m, id, p); - // fall through, with index == UINT_MAX it'll do the right thing. bool hasProperty = false; ReturnedValue v = virtualGet(m, id, m, &hasProperty); @@ -517,10 +511,9 @@ PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyK bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + const bool isArrayIndex = id.isArrayIndex(); + if (!isArrayIndex && !id.isCanonicalNumericIndexString()) return Object::virtualPut(m, id, value, receiver); - // fall through, with index == UINT_MAX it'll do the right thing. ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) @@ -531,6 +524,10 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu if (a->d()->buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); + if (!isArrayIndex) + return false; + + const uint index = id.asArrayIndex(); if (index >= a->length()) return false; @@ -547,11 +544,12 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) - return Object::virtualDefineOwnProperty(m, id, p, attrs); - // fall through, with index == UINT_MAX it'll do the right thing. + if (!id.isArrayIndex()) { + return !id.isCanonicalNumericIndexString() + && Object::virtualDefineOwnProperty(m, id, p, attrs); + } + const uint index = id.asArrayIndex(); TypedArray *a = static_cast<TypedArray *>(m); if (index >= a->length() || attrs.isAccessor()) return false; @@ -1595,7 +1593,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function R += separator; v = instance->get(k); - v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); s = v->toString(scope.engine); if (scope.hasException()) return Encode::undefined(); 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 diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index cbc153bb86..d29b060b9e 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -36,16 +36,14 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <qv4engine_p.h> + #include <qv4runtime_p.h> -#include <qv4string_p.h> #include <qv4propertykey_p.h> -#ifndef V4_BOOTSTRAP +#include <qv4string_p.h> #include <qv4symbol_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> -#endif #include <wtf/MathExtras.h> @@ -83,12 +81,8 @@ bool Value::toBooleanImpl(Value val) Heap::Base *b = val.m(); if (!b) return false; -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else if (b->internalClass->vtable->isString) return static_cast<Heap::String *>(b)->length() > 0; -#endif return true; } @@ -103,10 +97,6 @@ double Value::toNumberImpl(Value val) case QV4::Value::Undefined_Type: return std::numeric_limits<double>::quiet_NaN(); case QV4::Value::Managed_Type: -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - Q_FALLTHROUGH(); -#else if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); if (val.isSymbol()) { @@ -114,16 +104,15 @@ double Value::toNumberImpl(Value val) m.engine()->throwTypeError(); return 0; } - { - Q_ASSERT(val.isObject()); - Scope scope(val.objectValue()->engine()); - ScopedValue protectThis(scope, val); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); + { + Q_ASSERT(val.isObject()); + Scope scope(val.objectValue()->engine()); + ScopedValue protectThis(scope, val); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); } -#endif case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: @@ -133,7 +122,6 @@ double Value::toNumberImpl(Value val) } } -#ifndef V4_BOOTSTRAP QString Value::toQStringNoThrow() const { switch (type()) { @@ -248,7 +236,6 @@ QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const ScopedStringOrSymbol s(scope, v); return s->toPropertyKey(); } -#endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { if (_val == other._val) @@ -285,7 +272,6 @@ bool Value::sameValueZero(Value other) const { return false; } -#ifndef V4_BOOTSTRAP Heap::String *Value::toString(ExecutionEngine *e, Value val) { return RuntimeHelpers::convertToString(e, val); @@ -327,4 +313,3 @@ uint Value::asArrayLength(bool *ok) const } return idx; } -#endif // V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index ce85e48b72..4e901721cb 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -57,8 +57,10 @@ #include "qv4global_p.h" #include <private/qv4heap_p.h> #include <private/qv4internalclass_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qnumeric_p.h> +#include <private/qv4calldata_p.h> QT_BEGIN_NAMESPACE @@ -68,84 +70,23 @@ namespace Heap { struct Base; } -struct Q_QML_PRIVATE_EXPORT Value +struct Q_QML_PRIVATE_EXPORT Value : public StaticValue { - /* - 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()); } + using HeapBasePtr = Heap::Base *; + using ManagedPtr = Managed *; + + Value() = default; + constexpr Value(quint64 val) : StaticValue(val) {} + + static constexpr Value fromStaticValue(StaticValue staticValue) + { + return {staticValue._val}; + } #if QT_POINTER_SIZE == 8 - QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const + QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { - Heap::Base *b; + HeapBasePtr b; #ifdef __ia64 // Restore bits 49-47 to bits 63-61, undoing the workaround explained in // setM below. @@ -159,7 +100,7 @@ struct Q_QML_PRIVATE_EXPORT Value #endif return b; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b) + QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { memcpy(&_val, &b, 8); #ifdef __ia64 @@ -171,15 +112,15 @@ struct Q_QML_PRIVATE_EXPORT Value #endif } #elif QT_POINTER_SIZE == 4 - QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const + QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { - Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); - Heap::Base *b; + Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32)); + HeapBasePtr b; quint32 v = value(); memcpy(&b, &v, 4); return b; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b) + QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { quint32 v; memcpy(&v, &b, 4); @@ -189,190 +130,11 @@ struct Q_QML_PRIVATE_EXPORT Value # error "unsupported pointer size" #endif - 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 { return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); } - inline bool isManagedOrUndefined() const { return ((_val >> IsManagedOrUndefined_Shift) == 0); } - - 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(Value a, Value b) { - return a.integerCompatible() && b.integerCompatible(); - } - static inline bool bothDouble(Value a, Value 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; - Value 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 isString() const; inline bool isStringOrSymbol() const; inline bool isSymbol() const; inline bool isObject() const; inline bool isFunctionObject() const; - 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(); - } QML_NEARLY_ALWAYS_INLINE String *stringValue() const { if (!isString()) @@ -394,16 +156,16 @@ struct Q_QML_PRIVATE_EXPORT Value return nullptr; return reinterpret_cast<Object*>(const_cast<Value *>(this)); } - QML_NEARLY_ALWAYS_INLINE Managed *managed() const { + QML_NEARLY_ALWAYS_INLINE ManagedPtr managed() const { if (!isManaged()) return nullptr; return reinterpret_cast<Managed*>(const_cast<Value *>(this)); } - QML_NEARLY_ALWAYS_INLINE Heap::Base *heapObject() const { + QML_NEARLY_ALWAYS_INLINE Value::HeapBasePtr heapObject() const { return isManagedOrUndefined() ? m() : nullptr; } - static inline Value fromHeapObject(Heap::Base *m) + static inline Value fromHeapObject(HeapBasePtr m) { Value v; v.setM(m); @@ -446,12 +208,6 @@ struct Q_QML_PRIVATE_EXPORT Value static Heap::Object *toObject(ExecutionEngine *e, Value val); inline bool isPrimitive() const; - inline bool tryIntegerConversion() { - bool b = integerCompatible(); - if (b) - setTagValue(quint32(ValueTypeInternal::Integer), value()); - return b; - } template <typename T> const T *as() const { @@ -485,13 +241,12 @@ struct Q_QML_PRIVATE_EXPORT Value return static_cast<const T *>(managed()); } -#ifndef V4_BOOTSTRAP uint asArrayLength(bool *ok) const; -#endif - ReturnedValue *data_ptr() { return &_val; } - constexpr ReturnedValue asReturnedValue() const { return _val; } - static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } + static constexpr Value fromReturnedValue(ReturnedValue val) + { + return fromStaticValue(StaticValue::fromReturnedValue(val)); + } // As per ES specs bool sameValue(Value other) const; @@ -499,21 +254,45 @@ struct Q_QML_PRIVATE_EXPORT Value inline void mark(MarkStack *markStack); - inline static constexpr Value emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } - static inline constexpr Value fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } - static inline constexpr Value fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } - inline static constexpr Value undefinedValue() { return { 0 }; } - static inline constexpr Value nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } - static inline Value fromDouble(double d); - static inline Value fromUInt32(uint i); - - static double toInteger(double d); - static int toInt32(double d); - static unsigned int toUInt32(double d); + static double toInteger(double d) { return StaticValue::toInteger(d); } + static int toInt32(double d) { return StaticValue::toInt32(d); } + static unsigned int toUInt32(double d) { return StaticValue::toUInt32(d); } + inline static constexpr Value emptyValue() + { + return fromStaticValue(StaticValue::emptyValue()); + } + static inline constexpr Value fromBoolean(bool b) + { + return fromStaticValue(StaticValue::fromBoolean(b)); + } + static inline constexpr Value fromInt32(int i) + { + return fromStaticValue(StaticValue::fromInt32(i)); + } + inline static constexpr Value undefinedValue() + { + return fromStaticValue(StaticValue::undefinedValue()); + } + static inline constexpr Value nullValue() + { + return fromStaticValue(StaticValue::nullValue()); + } + static inline Value fromDouble(double d) + { + return fromStaticValue(StaticValue::fromDouble(d)); + } + static inline Value fromUInt32(uint i) + { + return fromStaticValue(StaticValue::fromUInt32(i)); + } Value &operator =(const ScopedValue &v); - Value &operator=(ReturnedValue v) { _val = v; return *this; } - Value &operator=(Managed *m) { + Value &operator=(ReturnedValue v) + { + StaticValue::operator=(v); + return *this; + } + Value &operator=(ManagedPtr m) { if (!m) { setM(nullptr); } else { @@ -521,7 +300,7 @@ struct Q_QML_PRIVATE_EXPORT Value } return *this; } - Value &operator=(Heap::Base *o) { + Value &operator=(HeapBasePtr o) { setM(o); return *this; } @@ -529,43 +308,88 @@ struct Q_QML_PRIVATE_EXPORT Value template<typename T> Value &operator=(const Scoped<T> &t); }; -Q_STATIC_ASSERT(std::is_trivial< Value >::value); +Q_STATIC_ASSERT(std::is_trivial<Value>::value); +Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue)); + +template<> +inline StaticValue &StaticValue::operator=<Value>(const Value &value) +{ + _val = value._val; + return *this; +} + +template<typename Managed> +inline StaticValue &StaticValue::operator=(const Managed &m) +{ + *static_cast<Value *>(this) = m; + return *this; +} + +template<> +inline Value &StaticValue::asValue<Value>() +{ + return *static_cast<Value *>(this); +} + +template<> +inline const Value &StaticValue::asValue<Value>() const +{ + return *static_cast<const Value *>(this); +} + +template<> +inline Value *CallData::argValues<Value>() +{ + return static_cast<Value *>(static_cast<StaticValue *>(args)); +} + +template<> +inline const Value *CallData::argValues<Value>() const +{ + return static_cast<const Value *>(static_cast<const StaticValue *>(args)); +} + +template<typename HeapBase> +inline Encode::Encode(HeapBase *o) +{ + val = Value::fromHeapObject(o).asReturnedValue(); +} inline void Value::mark(MarkStack *markStack) { - Heap::Base *o = heapObject(); + HeapBasePtr o = heapObject(); if (o) o->mark(markStack); } inline bool Value::isString() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isString; } bool Value::isStringOrSymbol() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol; } bool Value::isSymbol() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString; } inline bool Value::isObject() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isObject; } inline bool Value::isFunctionObject() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isFunctionObject; } @@ -595,151 +419,12 @@ inline ReturnedValue Value::convertedToNumber() const inline ReturnedValue Heap::Base::asReturnedValue() const { - return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); -} - -inline Value Value::fromDouble(double d) -{ - Value v; - v.setDouble(d); - return v; -} - -inline Value Value::fromUInt32(uint i) -{ - Value v; - if (i < INT_MAX) { - v.setTagValue(quint32(ValueTypeInternal::Integer), i); - } else { - v.setDouble(i); - } - return v; -} - -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); - } - } -}; - -inline double Value::toInteger(double d) -{ - if (std::isnan(d)) - return +0; - else if (!d || std::isinf(d)) - return d; - return d >= 0 ? std::floor(d) : std::ceil(d); -} - -inline int Value::toInt32(double value) -{ - return Double::toInt32(value); -} - -inline unsigned int Value::toUInt32(double d) -{ - return static_cast<uint>(toInt32(d)); + return Value::fromHeapObject(const_cast<Value::HeapBasePtr>(this)).asReturnedValue(); } // For source compat with older code in other modules using Primitive = Value; -struct Encode { - static constexpr ReturnedValue undefined() { - return Value::undefinedValue().asReturnedValue(); - } - static constexpr ReturnedValue null() { - return Value::nullValue().asReturnedValue(); - } - - explicit constexpr Encode(bool b) - : val(Value::fromBoolean(b).asReturnedValue()) - { - } - explicit Encode(double d) { - val = Value::fromDouble(d).asReturnedValue(); - } - explicit constexpr Encode(int i) - : val(Value::fromInt32(i).asReturnedValue()) - { - } - explicit Encode(uint i) { - val = Value::fromUInt32(i).asReturnedValue(); - } - explicit constexpr Encode(ReturnedValue v) - : val(v) - { - } - constexpr Encode(Value v) - : val(v.asReturnedValue()) - { - } - - explicit Encode(Heap::Base *o) { - val = Value::fromHeapObject(o).asReturnedValue(); - } - - explicit Encode(Value *o) { - Q_ASSERT(o); - val = o->asReturnedValue(); - } - - static ReturnedValue smallestNumber(double d) { - if (Value::isInt32(d)) - return Encode(static_cast<int>(d)); - else - return Encode(d); - } - - constexpr operator ReturnedValue() const { - return val; - } - quint64 val; -private: - explicit Encode(void *); -}; - template<typename T> ReturnedValue value_convert(ExecutionEngine *e, const Value &v); @@ -796,8 +481,8 @@ inline double Value::toInteger() const template <size_t o> struct HeapValue : Value { static Q_CONSTEXPR size_t offset = o; - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + HeapBasePtr base() { + HeapBasePtr base = reinterpret_cast<HeapBasePtr>(this) - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } @@ -805,7 +490,7 @@ struct HeapValue : Value { void set(EngineBase *e, const Value &newVal) { WriteBarrier::write(e, base(), data_ptr(), newVal.asReturnedValue()); } - void set(EngineBase *e, Heap::Base *b) { + void set(EngineBase *e, HeapBasePtr b) { WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue()); } }; @@ -817,8 +502,9 @@ struct ValueArray { uint alloc; Value values[1]; - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + Value::HeapBasePtr base() { + Value::HeapBasePtr base = reinterpret_cast<Value::HeapBasePtr>(this) + - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } @@ -826,7 +512,7 @@ struct ValueArray { void set(EngineBase *e, uint index, Value v) { WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue()); } - void set(EngineBase *e, uint index, Heap::Base *b) { + void set(EngineBase *e, uint index, Value::HeapBasePtr b) { WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } inline const Value &operator[] (uint index) const { @@ -855,12 +541,12 @@ struct ValueArray { const Value *end = v + alloc; if (alloc > 32*1024) { // drain from time to time to avoid overflows in the js stack - Heap::Base **currentBase = markStack->top; + Value::HeapBasePtr *currentBase = markStack->top; while (v < end) { v->mark(markStack); ++v; if (markStack->top >= currentBase + 32*1024) { - Heap::Base **oldBase = markStack->base; + Value::HeapBasePtr *oldBase = markStack->base; markStack->base = currentBase; markStack->drain(); markStack->base = oldBase; diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index e4d8bcaafc..e117e509ab 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -41,7 +41,6 @@ #include "qv4functionobject_p.h" #include "qv4objectproto_p.h" #include <private/qqmlvaluetypewrapper_p.h> -#include <private/qv8engine_p.h> #include <private/qv4qobjectwrapper_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 4ce8d97ee5..ad5a6ebc8c 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -38,11 +38,11 @@ ****************************************************************************/ #include "qv4vme_moth_p.h" -#include "qv4instr_moth_p.h" #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> +#include <private/qv4instr_moth_p.h> #include <private/qv4value_p.h> #include <private/qv4debugging_p.h> #include <private/qv4function_p.h> @@ -56,17 +56,20 @@ #include <private/qv4profiling_p.h> #include <private/qv4jscall_p.h> #include <private/qv4generatorobject_p.h> +#include <private/qv4alloca_p.h> #include <private/qqmljavascriptexpression_p.h> #include <iostream> -#include "qv4alloca_p.h" - +#if QT_CONFIG(qml_jit) #include <private/qv4baselinejit_p.h> +#endif #include <qtqml_tracepoints_p.h> #undef COUNT_INSTRUCTIONS +enum { ShowWhenDeoptimiationHappens = 0 }; + extern "C" { // This is the interface to Qt Creator's (new) QML debugger. @@ -202,7 +205,7 @@ int qt_v4DebuggerHook(const char *json) return ProtocolVersion; // Version number. } - int version = ob.value(QLatin1Literal("version")).toString().toInt(); + int version = ob.value(QLatin1String("version")).toString().toInt(); if (version != ProtocolVersion) { return -WrongProtocol; } @@ -345,73 +348,9 @@ static struct InstrCount { #undef CHECK_EXCEPTION #endif #define CHECK_EXCEPTION \ - if (engine->hasException) \ + if (engine->hasException || engine->isInterrupted.loadAcquire()) \ goto handleUnwind -static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken) - : quint8(ObservedTraceValues::FalsePathTaken); -#else - Q_UNUSED(truePathTaken); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceValue(ReturnedValue acc, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - switch (Primitive::fromReturnedValue(acc).type()) { - case QV4::Value::Integer_Type: - *traceInfo |= quint8(ObservedTraceValues::Integer); - break; - case QV4::Value::Boolean_Type: - *traceInfo |= quint8(ObservedTraceValues::Boolean); - break; - case QV4::Value::Double_Type: - *traceInfo |= quint8(ObservedTraceValues::Double); - break; - default: - *traceInfo |= quint8(ObservedTraceValues::Other); - break; - } -#else - Q_UNUSED(acc); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceDoubleValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Double); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceOtherValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Other); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); @@ -425,7 +364,7 @@ static inline Heap::CallContext *getScope(QV4::Value *stack, int level) static inline const QV4::Value &constant(Function *function, int index) { - return function->compilationUnit->constants[index]; + return function->compilationUnit->constants[index].asValue<QV4::Value>(); } static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) @@ -491,7 +430,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); -#ifdef V4_ENABLE_JIT +#if QT_CONFIG(qml_jit) if (debugger == nullptr) { if (function->jittedCode == nullptr) { if (engine->canJIT(function)) @@ -499,16 +438,20 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) else ++function->interpreterCallCount; } - if (function->jittedCode != nullptr) - return function->jittedCode(frame, engine); } -#endif // V4_ENABLE_JIT +#endif // QT_CONFIG(qml_jit) // interpreter if (debugger) debugger->enteringFunction(); - ReturnedValue result = interpret(frame, engine, function->codeData); + ReturnedValue result; + if (function->jittedCode != nullptr && debugger == nullptr) { + result = function->jittedCode(frame, engine); + } else { + // interpreter + result = interpret(frame, engine, function->codeData); + } if (debugger) debugger->leavingFunction(result); @@ -519,15 +462,10 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code) { QV4::Function *function = frame->v4Function; - QV4::Value &accumulator = frame->jsFrame->accumulator; + QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>(); QV4::ReturnedValue acc = accumulator.asReturnedValue(); Value *stack = reinterpret_cast<Value *>(frame->jsFrame); - if (function->tracingEnabled()) { - for (int i = 0; i < int(function->nFormals); ++i) - traceValue(frame->jsFrame->argument(i), function, i); - } - MOTH_JUMP_TABLE; for (;;) { @@ -586,7 +524,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -599,7 +536,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadScopedLocal) auto cc = getScope(stack, scope); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -613,88 +549,73 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(MoveRegExp) - STACK_VALUE(destReg) = Runtime::method_regexpLiteral(engine, regExpId); + STACK_VALUE(destReg) = Runtime::RegexpLiteral::call(engine, regExpId); MOTH_END_INSTR(MoveRegExp) MOTH_BEGIN_INSTR(LoadClosure) - acc = Runtime::method_closure(engine, value); + acc = Runtime::Closure::call(engine, value); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) STORE_IP(); - acc = Runtime::method_loadName(engine, name); + acc = Runtime::LoadName::call(engine, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) STORE_IP(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; acc = l->globalGetter(l, engine); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) STORE_IP(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; acc = l->qmlContextPropertyGetter(l, engine, nullptr); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameStrict) STORE_IP(); STORE_ACC(); - Runtime::method_storeNameStrict(engine, name, accumulator); + Runtime::StoreNameStrict::call(engine, name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(StoreNameSloppy) STORE_IP(); STORE_ACC(); - Runtime::method_storeNameSloppy(engine, name, accumulator); + Runtime::StoreNameSloppy::call(engine, name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreNameSloppy) MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - acc = Runtime::method_loadElement_traced(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot)); - traceValue(acc, function, traceSlot); -#else - Q_UNUSED(traceSlot); - acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); -#endif + acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - Runtime::method_storeElement_traced(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot)); -#else - Q_UNUSED(traceSlot); - Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); -#endif + Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) STORE_IP(); STORE_ACC(); - acc = Runtime::method_loadProperty(engine, accumulator, name); + acc = Runtime::LoadProperty::call(engine, accumulator, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) STORE_IP(); STORE_ACC(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; if (accumulator.isNullOrUndefined()) { QString message = QStringLiteral("Cannot read property '%1' of %2") @@ -706,20 +627,19 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) STORE_IP(); STORE_ACC(); - Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator); + Runtime::StoreProperty::call(engine, STACK_VALUE(base), name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) STORE_IP(); STORE_ACC(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict()) engine->throwTypeError(); CHECK_EXCEPTION; @@ -727,14 +647,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadSuperProperty) STORE_IP(); - acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property)); + acc = Runtime::LoadSuperProperty::call(engine, STACK_VALUE(property)); CHECK_EXCEPTION; MOTH_END_INSTR(LoadSuperProperty) MOTH_BEGIN_INSTR(StoreSuperProperty) STORE_IP(); STORE_ACC(); - Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator); + Runtime::StoreSuperProperty::call(engine, STACK_VALUE(property), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreSuperProperty) @@ -765,7 +685,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(IteratorNextForYieldStar) STORE_ACC(); - acc = Runtime::method_iteratorNextForYieldStar(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object)); + acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object)); CHECK_EXCEPTION; MOTH_END_INSTR(IteratorNextForYieldStar) @@ -779,7 +699,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, Value undef = Value::undefinedValue(); acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) @@ -791,19 +710,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) STORE_IP(); - acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc); + acc = Runtime::CallProperty::call(engine, stack[base], name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) STORE_IP(); - Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex; + Lookup *l = function->executableCompilationUnit()->runtimeLookups + lookupIndex; if (stack[base].isNullOrUndefined()) { QString message = QStringLiteral("Cannot call method '%1' of %2") @@ -826,49 +743,42 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) STORE_IP(); - acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc); + acc = Runtime::CallElement::call(engine, stack[base], STACK_VALUE(index), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) STORE_IP(); - acc = Runtime::method_callName(engine, name, stack + argv, argc); + acc = Runtime::CallName::call(engine, name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) STORE_IP(); - acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc); + acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) STORE_IP(); - acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc); + acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) STORE_IP(); - acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc); + acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); - acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); + acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(TailCall) @@ -877,21 +787,21 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, *engine->jsAlloca(1) = Primitive::fromInt32(argv); *engine->jsAlloca(1) = STACK_VALUE(thisObject); *engine->jsAlloca(1) = STACK_VALUE(func); - return Runtime::method_tailCall(frame, engine); + return Runtime::TailCall::call(frame, engine); CHECK_EXCEPTION; MOTH_END_INSTR(TailCall) MOTH_BEGIN_INSTR(Construct) STORE_IP(); STORE_ACC(); - acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc); + acc = Runtime::Construct::call(engine, STACK_VALUE(func), ACC, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(Construct) MOTH_BEGIN_INSTR(ConstructWithSpread) STORE_IP(); STORE_ACC(); - acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, stack + argv, argc); + acc = Runtime::ConstructWithSpread::call(engine, STACK_VALUE(func), ACC, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(ConstructWithSpread) @@ -918,7 +828,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(DeadTemporalZoneCheck) if (ACC.isEmpty()) { STORE_IP(); - Runtime::method_throwReferenceError(engine, name); + Runtime::ThrowReferenceError::call(engine, name); goto handleUnwind; } MOTH_END_INSTR(DeadTemporalZoneCheck) @@ -926,7 +836,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(ThrowException) STORE_IP(); STORE_ACC(); - Runtime::method_throwException(engine, accumulator); + Runtime::ThrowException::call(engine, accumulator); goto handleUnwind; MOTH_END_INSTR(ThrowException) @@ -944,40 +854,36 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(SetException) MOTH_BEGIN_INSTR(PushCatchContext) - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name); + Runtime::PushCatchContext::call(engine, index, name); MOTH_END_INSTR(PushCatchContext) MOTH_BEGIN_INSTR(CreateCallContext) - stack[CallData::Context] = ExecutionContext::newCallContext(frame); + Runtime::PushCallContext::call(frame); MOTH_END_INSTR(CreateCallContext) MOTH_BEGIN_INSTR(PushWithContext) STORE_IP(); STORE_ACC(); - auto ctx = Runtime::method_createWithContext(engine, stack); + acc = Runtime::PushWithContext::call(engine, stack[CallData::Accumulator]); CHECK_EXCEPTION; - STACK_VALUE(CallData::Context) = ctx; MOTH_END_INSTR(PushWithContext) MOTH_BEGIN_INSTR(PushBlockContext) STORE_ACC(); - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index); + Runtime::PushBlockContext::call(engine, index); MOTH_END_INSTR(PushBlockContext) MOTH_BEGIN_INSTR(CloneBlockContext) STORE_ACC(); - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c); + Runtime::CloneBlockContext::call(engine); MOTH_END_INSTR(CloneBlockContext) MOTH_BEGIN_INSTR(PushScriptContext) - STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index); + Runtime::PushScriptContext::call(engine, index); MOTH_END_INSTR(PushScriptContext) MOTH_BEGIN_INSTR(PopScriptContext) - STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine); + Runtime::PopScriptContext::call(engine); MOTH_END_INSTR(PopScriptContext) MOTH_BEGIN_INSTR(PopContext) @@ -988,14 +894,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(GetIterator) STORE_IP(); STORE_ACC(); - acc = Runtime::method_getIterator(engine, accumulator, iterator); + acc = Runtime::GetIterator::call(engine, accumulator, iterator); CHECK_EXCEPTION; MOTH_END_INSTR(GetIterator) MOTH_BEGIN_INSTR(IteratorNext) STORE_IP(); STORE_ACC(); - acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value)); + acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value)); STACK_VALUE(done) = acc; CHECK_EXCEPTION; MOTH_END_INSTR(IteratorNext) @@ -1003,98 +909,74 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(IteratorClose) STORE_IP(); STORE_ACC(); - acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done)); + acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done)); CHECK_EXCEPTION; MOTH_END_INSTR(IteratorClose) MOTH_BEGIN_INSTR(DestructureRestElement) STORE_IP(); STORE_ACC(); - acc = Runtime::method_destructureRestElement(engine, ACC); + acc = Runtime::DestructureRestElement::call(engine, ACC); CHECK_EXCEPTION; MOTH_END_INSTR(DestructureRestElement) MOTH_BEGIN_INSTR(DeleteProperty) - if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) { - if (function->isStrict()) { - STORE_IP(); - engine->throwTypeError(); - goto handleUnwind; - } - acc = Encode(false); - } else { - acc = Encode(true); - } + acc = Runtime::DeleteProperty::call(engine, function, STACK_VALUE(base), STACK_VALUE(index)); + CHECK_EXCEPTION; MOTH_END_INSTR(DeleteProperty) MOTH_BEGIN_INSTR(DeleteName) - if (!Runtime::method_deleteName(engine, name)) { - if (function->isStrict()) { - STORE_IP(); - QString n = function->compilationUnit->runtimeStrings[name]->toQString(); - engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n)); - goto handleUnwind; - } - acc = Encode(false); - } else { - acc = Encode(true); - } + acc = Runtime::DeleteName::call(engine, function, name); + CHECK_EXCEPTION; MOTH_END_INSTR(DeleteName) MOTH_BEGIN_INSTR(TypeofName) - acc = Runtime::method_typeofName(engine, name); + acc = Runtime::TypeofName::call(engine, name); MOTH_END_INSTR(TypeofName) MOTH_BEGIN_INSTR(TypeofValue) STORE_ACC(); - acc = Runtime::method_typeofValue(engine, accumulator); + acc = Runtime::TypeofValue::call(engine, accumulator); MOTH_END_INSTR(TypeofValue) MOTH_BEGIN_INSTR(DeclareVar) - Runtime::method_declareVar(engine, isDeletable, varName); + Runtime::DeclareVar::call(engine, isDeletable, varName); MOTH_END_INSTR(DeclareVar) MOTH_BEGIN_INSTR(DefineArray) QV4::Value *arguments = stack + args; - acc = Runtime::method_arrayLiteral(engine, arguments, argc); + acc = Runtime::ArrayLiteral::call(engine, arguments, argc); MOTH_END_INSTR(DefineArray) MOTH_BEGIN_INSTR(DefineObjectLiteral) QV4::Value *arguments = stack + args; - acc = Runtime::method_objectLiteral(engine, internalClassId, arguments, argc); + acc = Runtime::ObjectLiteral::call(engine, internalClassId, arguments, argc); MOTH_END_INSTR(DefineObjectLiteral) MOTH_BEGIN_INSTR(CreateClass) - acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); + acc = Runtime::CreateClass::call(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); MOTH_END_INSTR(CreateClass) MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) - acc = Runtime::method_createMappedArgumentsObject(engine); + acc = Runtime::CreateMappedArgumentsObject::call(engine); MOTH_END_INSTR(CreateMappedArgumentsObject) MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) - acc = Runtime::method_createUnmappedArgumentsObject(engine); + acc = Runtime::CreateUnmappedArgumentsObject::call(engine); MOTH_END_INSTR(CreateUnmappedArgumentsObject) MOTH_BEGIN_INSTR(CreateRestParameter) - acc = Runtime::method_createRestParameter(engine, argIndex); + acc = Runtime::CreateRestParameter::call(engine, argIndex); MOTH_END_INSTR(CreateRestParameter) MOTH_BEGIN_INSTR(ConvertThisToObject) - Value *t = &stack[CallData::This]; - if (!t->isObject()) { - if (t->isNullOrUndefined()) { - *t = engine->globalObject->asReturnedValue(); - } else { - STORE_ACC(); - *t = t->toObject(engine)->asReturnedValue(); - CHECK_EXCEPTION; - } - } + STORE_ACC(); + stack[CallData::This] = Runtime::ConvertThisToObject::call(engine, stack[CallData::This]); + CHECK_EXCEPTION; MOTH_END_INSTR(ConvertThisToObject) MOTH_BEGIN_INSTR(LoadSuperConstructor) - acc = Runtime::method_loadSuperConstructor(engine, stack[CallData::Function]); + acc = Runtime::LoadSuperConstructor::call(engine, stack[CallData::Function]); CHECK_EXCEPTION; MOTH_END_INSTR(LoadSuperConstructor) @@ -1114,7 +996,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = ACC.int_32(); else takeJump = ACC.toBoolean(); - traceJumpTakesTruePath(takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpTrue) @@ -1125,7 +1006,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = !ACC.int_32(); else takeJump = !ACC.toBoolean(); - traceJumpTakesTruePath(!takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpFalse) @@ -1140,6 +1020,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, code += offset; MOTH_END_INSTR(JumpNotUndefined) + MOTH_BEGIN_INSTR(CheckException) + CHECK_EXCEPTION; + MOTH_END_INSTR(CheckException) + MOTH_BEGIN_INSTR(CmpEqNull) acc = Encode(ACC.isNullOrUndefined()); MOTH_END_INSTR(CmpEqNull) @@ -1176,7 +1060,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.int_32() == ACC.int_32()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareEqual(left, accumulator))); + acc = Encode(bool(Runtime::CompareEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpEq) @@ -1187,7 +1071,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(bool(left.int_32() != ACC.int_32())); } else { STORE_ACC(); - acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator))); + acc = Encode(bool(!Runtime::CompareEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpNe) @@ -1200,7 +1084,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() > ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareGreaterThan(left, accumulator))); + acc = Encode(bool(Runtime::CompareGreaterThan::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpGt) @@ -1213,7 +1097,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() >= ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareGreaterEqual(left, accumulator))); + acc = Encode(bool(Runtime::CompareGreaterEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpGe) @@ -1226,7 +1110,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() < ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareLessThan(left, accumulator))); + acc = Encode(bool(Runtime::CompareLessThan::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpLt) @@ -1239,7 +1123,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() <= ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareLessEqual(left, accumulator))); + acc = Encode(bool(Runtime::CompareLessEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpLe) @@ -1249,7 +1133,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(true); } else { STORE_ACC(); - acc = Encode(bool(RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator))); + acc = Runtime::StrictEqual::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpStrictEqual) @@ -1257,7 +1141,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(CmpStrictNotEqual) if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) { STORE_ACC(); - acc = Encode(!RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator)); + acc = Runtime::StrictNotEqual::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; } else { acc = Encode(false); @@ -1266,13 +1150,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(CmpIn) STORE_ACC(); - acc = Runtime::method_in(engine, STACK_VALUE(lhs), accumulator); + acc = Runtime::In::call(engine, STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(CmpIn) MOTH_BEGIN_INSTR(CmpInstanceOf) STORE_ACC(); - acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC); + acc = Runtime::Instanceof::call(engine, STACK_VALUE(lhs), ACC); CHECK_EXCEPTION; MOTH_END_INSTR(CmpInstanceOf) @@ -1296,17 +1180,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, int a = ACC.int_32(); if (a == 0 || a == std::numeric_limits<int>::min()) { acc = Encode(-static_cast<double>(a)); - traceDoubleValue(function, traceSlot); } else { - acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(0, ACC.int_32()); } } else if (ACC.isDouble()) { acc ^= (1ull << 63); // simply flip sign bit - traceDoubleValue(function, traceSlot); } else { acc = Encode(-ACC.toNumberImpl()); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(UMinus) @@ -1317,57 +1198,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Increment) if (Q_LIKELY(ACC.integerCompatible())) { - acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = add_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() + 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() + 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) if (Q_LIKELY(ACC.integerCompatible())) { - acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = sub_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() - 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() - 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = add_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() + ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); - acc = Runtime::method_add(engine, left, accumulator); + acc = Runtime::Add::call(engine, left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(Sub) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() - ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); - acc = Runtime::method_sub(left, accumulator); + acc = Runtime::Sub::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Sub) @@ -1376,7 +1249,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, double base = left.toNumber(); double exp = ACC.toNumber(); if (qIsInf(exp) && (base == 1 || base == -1)) - acc = Encode(qSNaN()); + acc = Encode(qQNaN()); else acc = Encode(pow(base,exp)); MOTH_END_INSTR(Exp) @@ -1384,29 +1257,26 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = mul_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() * ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); - acc = Runtime::method_mul(left, accumulator); + acc = Runtime::Mul::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) STORE_ACC(); - acc = Runtime::method_div(STACK_VALUE(lhs), accumulator); + acc = Runtime::Div::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) STORE_ACC(); - acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator); + acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(BitAnd) @@ -1493,7 +1363,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(ThrowOnNullOrUndefined) MOTH_BEGIN_INSTR(GetTemplateObject) - acc = RuntimeHelpers::getTemplateObject(function, index); + acc = Runtime::GetTemplateObject::call(function, index); MOTH_END_INSTR(GetTemplateObject) MOTH_BEGIN_INSTR(Debug) @@ -1504,7 +1374,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(Debug) handleUnwind: - Q_ASSERT(engine->hasException || frame->unwindLevel); + // We do start the exception handler in case of isInterrupted. The exception handler will + // immediately abort, due to the same isInterrupted. We don't skip the exception handler + // because the current behavior is easier to implement in the JIT. + Q_ASSERT(engine->hasException || engine->isInterrupted.loadAcquire() || frame->unwindLevel); if (!frame->unwindHandler) { acc = Encode::undefined(); return acc; diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 8a76e60f20..b3944f5454 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -52,6 +52,7 @@ // #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h index aff1ae82d7..9dda104cd1 100644 --- a/src/qml/jsruntime/qv4vtable_p.h +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -58,7 +58,7 @@ namespace QV4 { struct Lookup; -struct OwnPropertyKeyIterator { +struct Q_QML_PRIVATE_EXPORT OwnPropertyKeyIterator { virtual ~OwnPropertyKeyIterator() = 0; virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0; }; @@ -90,8 +90,8 @@ struct VTable typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &); const VTable * const parent; - quint32 inlinePropertyOffset : 16; - quint32 nInlineProperties : 16; + quint16 inlinePropertyOffset; + quint16 nInlineProperties; quint8 isExecutionContext; quint8 isString; quint8 isObject; |