aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4engine.cpp')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2063
1 files changed, 1429 insertions, 634 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index b6000dbcca..bd6251caa9 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1,57 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#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>
-
-#ifndef V4_BOOTSTRAP
+#include <QLoggingCategory>
+#if QT_CONFIG(regularexpression)
+#include <QRegularExpression>
+#endif
+#include <QtCore/QTimeZone>
+#include <QtCore/qiterable.h>
#include <qv4qmlcontext_p.h>
#include <qv4value_p.h>
@@ -91,19 +55,18 @@
#include "qv4reflect_p.h"
#include "qv4proxy_p.h"
#include "qv4stackframe_p.h"
+#include "qv4stacklimits_p.h"
#include "qv4atomics_p.h"
-
-#if QT_CONFIG(qml_sequence_object)
+#include "qv4urlobject_p.h"
+#include "qv4variantobject_p.h"
#include "qv4sequenceobject_p.h"
-#endif
-
#include "qv4qobjectwrapper_p.h"
+#include "qv4qmetaobjectwrapper_p.h"
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
#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>
@@ -111,41 +74,261 @@
#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>
+#include <qmetatype.h>
+#include <qsequentialiterable.h>
-#if USE(PTHREADS)
-# include <pthread.h>
-#if !defined(Q_OS_INTEGRITY)
-# include <sys/resource.h>
-#endif
-#if HAVE(PTHREAD_NP_H)
-# include <pthread_np.h>
-#endif
-#endif
+#include <private/qqmlengine_p.h>
#ifdef V4_USE_VALGRIND
#include <valgrind/memcheck.h>
#endif
-#endif // #ifndef V4_BOOTSTRAP
-
QT_BEGIN_NAMESPACE
-using namespace QV4;
+DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
+DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
-#ifndef V4_BOOTSTRAP
+using namespace QV4;
+// While engineSerial is odd the statics haven't been initialized. The engine that receives ID 1
+// initializes the statics and sets engineSerial to 2 afterwards.
+// Each engine does engineSerial.fetchAndAddOrdered(2) on creation. Therefore engineSerial stays
+// odd while the statics are being initialized, and stays even afterwards.
+// Any further engines created while the statics are being initialized busy-wait until engineSerial
+// is even.
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+int ExecutionEngine::s_maxCallDepth = -1;
+int ExecutionEngine::s_jitCallCountThreshold = 3;
+int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
+int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024;
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
{
return b->engine()->throwTypeError();
}
-qint32 ExecutionEngine::maxCallDepth = -1;
+
+template <typename ReturnType>
+ReturnType convertJSValueToVariantType(const QJSValue &value)
+{
+ const QVariant variant = value.toVariant();
+ return variant.metaType() == QMetaType::fromType<QJSValue>()
+ ? ReturnType()
+ : variant.value<ReturnType>();
+}
+
+struct JSArrayIterator {
+ QJSValue const* data;
+ quint32 index;
+};
+
+namespace {
+void createNewIteratorIfNonExisting(void **iterator) {
+ if (*iterator == nullptr)
+ *iterator = new JSArrayIterator;
+}
+}
+
+static QtMetaContainerPrivate::QMetaSequenceInterface emptySequenceInterface()
+{
+ // set up some functions so that non-array QSequentialIterables do not crash
+ // but instead appear as an empty sequence
+
+ using namespace QtMetaContainerPrivate;
+ QMetaSequenceInterface iface;
+ iface.sizeFn = [](const void *) { return qsizetype(0); };
+ iface.valueAtIndexFn = [](const void *, qsizetype, void *) {};
+ iface.createIteratorFn = [](void *, QMetaSequenceInterface::Position) -> void * {
+ return nullptr;
+ };
+ iface.advanceIteratorFn = [](void *, qsizetype) {};
+ iface.compareIteratorFn = [](const void *, const void *) {
+ return true; /*all iterators are nullptr*/
+ };
+ iface.destroyIteratorFn = [](const void *) {};
+ iface.copyIteratorFn = [](void *, const void *) {};
+ iface.diffIteratorFn = [](const void *, const void *) { return qsizetype(0); };
+ return iface;
+}
+
+static QtMetaContainerPrivate::QMetaSequenceInterface sequenceInterface()
+{
+ using namespace QtMetaContainerPrivate;
+ QMetaSequenceInterface iface;
+ iface.valueMetaType = QtPrivate::qMetaTypeInterfaceForType<QVariant>();
+ iface.iteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability;
+ iface.addRemoveCapabilities = CanAddAtEnd;
+ iface.sizeFn = [](const void *p) -> qsizetype {
+ return static_cast<QJSValue const *>(p)->property(QString::fromLatin1("length")).toInt();
+ };
+
+ /* Lifetime management notes:
+ * valueAtIndexFn and valueAtIteratorFn return a pointer to a JSValue allocated via
+ * QMetaType::create Because we set QVariantConstructionFlags::ShouldDeleteVariantData,
+ * QSequentialIterable::at and QSequentialIterable::operator*() will free that memory
+ */
+
+ iface.valueAtIndexFn = [](const void *iterable, qsizetype index, void *dataPtr) -> void {
+ auto *data = static_cast<QVariant *>(dataPtr);
+ *data = static_cast<QJSValue const *>(iterable)->property(quint32(index)).toVariant();
+ };
+ iface.createIteratorFn = [](void *iterable, QMetaSequenceInterface::Position pos) {
+ void *iterator = nullptr;
+ createNewIteratorIfNonExisting(&iterator);
+ auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator);
+ jsArrayIterator->index = 0;
+ jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
+ if (pos == QMetaSequenceInterface::AtEnd) {
+ auto length = static_cast<QJSValue const *>(iterable)->property(
+ QString::fromLatin1("length")).toInt();
+ jsArrayIterator->index = quint32(length);
+ }
+ return iterator;
+ };
+ iface.createConstIteratorFn = [](const void *iterable, QMetaSequenceInterface::Position pos) {
+ void *iterator = nullptr;
+ createNewIteratorIfNonExisting(&iterator);
+ auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator);
+ jsArrayIterator->index = 0;
+ jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
+ if (pos == QMetaSequenceInterface::AtEnd) {
+ auto length = static_cast<QJSValue const *>(iterable)->property(
+ QString::fromLatin1("length")).toInt();
+ jsArrayIterator->index = quint32(length);
+ }
+ return iterator;
+ };
+ iface.advanceIteratorFn = [](void *iterator, qsizetype advanceBy) {
+ static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy);
+ };
+ iface.advanceConstIteratorFn = [](void *iterator, qsizetype advanceBy) {
+ static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy);
+ };
+ iface.valueAtIteratorFn = [](const void *iterator, void *dataPtr) -> void {
+ const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator);
+ const QJSValue *jsArray = arrayIterator->data;
+ auto *data = static_cast<QVariant *>(dataPtr);
+ *data = jsArray->property(arrayIterator->index).toVariant();
+ };
+ iface.valueAtConstIteratorFn = [](const void *iterator, void *dataPtr) -> void {
+ const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator);
+ const QJSValue *jsArray = arrayIterator->data;
+ auto *data = static_cast<QVariant *>(dataPtr);
+ *data = jsArray->property(arrayIterator->index).toVariant();
+ };
+ iface.destroyIteratorFn = [](const void *iterator) {
+ delete static_cast<const JSArrayIterator *>(iterator);
+ };
+ iface.destroyConstIteratorFn = [](const void *iterator) {
+ delete static_cast<const JSArrayIterator *>(iterator);
+ };
+ iface.compareIteratorFn = [](const void *p, const void *other) {
+ auto this_ = static_cast<const JSArrayIterator *>(p);
+ auto that_ = static_cast<const JSArrayIterator *>(other);
+ return this_->index == that_->index && this_->data == that_->data;
+ };
+ iface.compareConstIteratorFn = [](const void *p, const void *other) {
+ auto this_ = static_cast<const JSArrayIterator *>(p);
+ auto that_ = static_cast<const JSArrayIterator *>(other);
+ return this_->index == that_->index && this_->data == that_->data;
+ };
+ iface.copyIteratorFn = [](void *iterator, const void *otherIterator) {
+ auto *otherIter = (static_cast<JSArrayIterator const *>(otherIterator));
+ static_cast<JSArrayIterator *>(iterator)->index = otherIter->index;
+ static_cast<JSArrayIterator *>(iterator)->data = otherIter->data;
+ };
+ iface.copyConstIteratorFn = [](void *iterator, const void *otherIterator) {
+ auto *otherIter = (static_cast<JSArrayIterator const *>(otherIterator));
+ static_cast<JSArrayIterator *>(iterator)->index = otherIter->index;
+ static_cast<JSArrayIterator *>(iterator)->data = otherIter->data;
+ };
+ iface.diffIteratorFn = [](const void *iterator, const void *otherIterator) -> qsizetype {
+ const auto *self = static_cast<const JSArrayIterator *>(iterator);
+ const auto *other = static_cast<const JSArrayIterator *>(otherIterator);
+ return self->index - other->index;
+ };
+ iface.diffConstIteratorFn = [](const void *iterator, const void *otherIterator) -> qsizetype {
+ const auto *self = static_cast<const JSArrayIterator *>(iterator);
+ const auto *other = static_cast<const JSArrayIterator *>(otherIterator);
+ return self->index - other->index;
+ };
+ iface.addValueFn = [](void *iterable, const void *data, QMetaSequenceInterface::Position) {
+ auto *jsvalue = static_cast<QJSValue *>(iterable);
+ QV4::Scope scope(QJSValuePrivate::engine(jsvalue));
+ QV4::ScopedArrayObject a(scope, QJSValuePrivate::asManagedType<QV4::ArrayObject>(jsvalue));
+ QV4::ScopedValue v(scope, scope.engine->fromVariant(*static_cast<const QVariant *>(data)));
+ if (!a)
+ return;
+ int len = a->getLength();
+ a->setIndexed(len, v, QV4::Object::DoNotThrow);
+ };
+ return iface;
+}
+
+static QSequentialIterable jsvalueToSequence (const QJSValue& value) {
+ using namespace QtMetaTypePrivate;
+ using namespace QtMetaContainerPrivate;
+
+
+ if (!value.isArray()) {
+ static QMetaSequenceInterface emptySequence = emptySequenceInterface();
+ return QSequentialIterable(QMetaSequence(&emptySequence), nullptr);
+ }
+
+ static QMetaSequenceInterface sequence = sequenceInterface();
+ return QSequentialIterable(QMetaSequence(&sequence), &value);
+}
+
+void ExecutionEngine::initializeStaticMembers()
+{
+ bool ok = false;
+
+ const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxJSStackSize > 0)
+ s_maxJSStackSize = envMaxJSStackSize;
+
+ const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok);
+ if (ok && envMaxGCStackSize > 0)
+ s_maxGCStackSize = envMaxGCStackSize;
+
+ if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) {
+ s_maxCallDepth = std::numeric_limits<qint32>::max();
+ } else {
+ ok = false;
+ s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
+ if (!ok || s_maxCallDepth <= 0)
+ s_maxCallDepth = -1;
+ }
+
+ ok = false;
+ s_jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
+ if (!ok)
+ s_jitCallCountThreshold = 3;
+ if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
+ s_jitCallCountThreshold = std::numeric_limits<int>::max();
+
+ 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>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>())
+ QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence);
+}
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
@@ -154,56 +337,58 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
, jsStack(new WTF::PageAllocation)
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
- , v8Engine(nullptr)
, publicEngine(jsEngine)
- , m_engineId(engineSerial.fetchAndAddOrdered(1))
+ , m_engineId(engineSerial.fetchAndAddOrdered(2))
, regExpCache(nullptr)
, m_multiplyWrappedQObjects(nullptr)
-#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP)
+#if QT_CONFIG(qml_jit)
, m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory())
#endif
-{
- memoryManager = new QV4::MemoryManager(this);
-
- if (maxCallDepth == -1) {
- bool 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)
- maxCallDepth = 1234;
-#else
- // no (tail call) optimization is done, so there'll be a lot mare stack frames active
- maxCallDepth = 200;
+#if QT_CONFIG(qml_xml_http_request)
+ , m_xmlHttpRequestData(nullptr)
#endif
+ , m_qmlEngine(nullptr)
+{
+ if (m_engineId == 1) {
+ initializeStaticMembers();
+ engineSerial.storeRelease(2); // make it even
+ } else if (Q_UNLIKELY(m_engineId & 1)) {
+ // This should be rare. You usually don't create lots of engines at the same time.
+ while (engineSerial.loadAcquire() & 1) {
+ QThread::yieldCurrentThread();
}
}
- Q_ASSERT(maxCallDepth > 0);
+ if (s_maxCallDepth < 0) {
+ const StackProperties stack = stackProperties();
+ cppStackBase = stack.base;
+ cppStackLimit = stack.softLimit;
+ } else {
+ callDepth = 0;
+ }
+
+ // We allocate guard pages around our stacks.
+ const size_t guardPages = 2 * WTF::pageSize();
+
+ memoryManager = new QV4::MemoryManager(this);
+ // we don't want to run the gc while the initial setup is not done; not even in aggressive mode
+ GCCriticalSection gcCriticalSection(this);
// 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,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
+ *jsStack = WTF::PageAllocation::allocate(
+ s_maxJSStackSize + 256*1024 + guardPages, 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,
- /* writable */ true, /* executable */ false,
- /* includesGuardPages */ true);
-
- {
- bool ok = false;
- jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
- if (!ok)
- jitCallCountThreshold = 3;
- if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"))
- jitCallCountThreshold = std::numeric_limits<int>::max();
- }
+ *gcStack = WTF::PageAllocation::allocate(
+ s_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages,
+ /* writable */ true, /* executable */ false, /* includesGuardPages */ true);
exceptionValue = jsAlloca(1);
*exceptionValue = Encode::undefined();
@@ -215,7 +400,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
- jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value);
+ jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value);
identifierTable = new IdentifierTable(this);
@@ -439,30 +624,26 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
-#if QT_CONFIG(qml_sequence_object)
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
-#endif
- ExecutionContext *global = rootContext();
-
- jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global);
- jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global);
- jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global);
- jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global);
- jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global);
- jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global);
- jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global);
- jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global);
- jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global);
- jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global);
- jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global);
- jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global);
- jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global);
- jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global);
- jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global);
- jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global);
- jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global);
+ jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(this);
+ jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(this);
+ jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(this);
+ jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(this);
+ jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(this);
+ jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(this);
+ jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(this);
+ jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(this);
+ jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(this);
+ jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(this);
+ jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(this);
+ jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(this);
+ jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(this);
+ jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(this);
+ jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(this);
+ jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(this);
+ jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(this);
jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype());
@@ -476,6 +657,15 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype());
jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic);
+ //
+ // url
+ //
+
+ jsObjects[Url_Ctor] = memoryManager->allocate<UrlCtor>(this);
+ jsObjects[UrlProto] = memoryManager->allocate<UrlPrototype>();
+ jsObjects[UrlSearchParams_Ctor] = memoryManager->allocate<UrlSearchParamsCtor>(this);
+ jsObjects[UrlSearchParamsProto] = memoryManager->allocate<UrlSearchParamsPrototype>();
+
str = newString(QStringLiteral("get [Symbol.species]"));
jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
@@ -485,7 +675,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
- static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this);
+ static_cast<PropertyListPrototype *>(propertyListPrototype())->init();
static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
@@ -497,6 +687,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor());
static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
+ static_cast<UrlPrototype *>(urlPrototype())->init(this, urlCtor());
+ static_cast<UrlSearchParamsPrototype *>(urlSearchParamsPrototype())->init(this, urlSearchParamsCtor());
static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
@@ -507,23 +699,21 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
static_cast<VariantPrototype *>(variantPrototype())->init();
-#if QT_CONFIG(qml_sequence_object)
sequencePrototype()->cast<SequencePrototype>()->init();
-#endif
- jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(global);
+ jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(this);
jsObjects[WeakMapProto] = memoryManager->allocate<WeakMapPrototype>();
static_cast<WeakMapPrototype *>(weakMapPrototype())->init(this, weakMapCtor());
- jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global);
+ jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(this);
jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
- jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(global);
+ jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(this);
jsObjects[WeakSetProto] = memoryManager->allocate<WeakSetPrototype>();
static_cast<WeakSetPrototype *>(weakSetPrototype())->init(this, weakSetCtor());
- jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global);
+ jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(this);
jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
@@ -531,33 +721,34 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
// promises
//
- jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(global);
+ jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(this);
jsObjects[PromiseProto] = memoryManager->allocate<PromisePrototype>();
static_cast<PromisePrototype *>(promisePrototype())->init(this, promiseCtor());
// typed arrays
- jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(global);
+ jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(this);
jsObjects[SharedArrayBufferProto] = memoryManager->allocate<SharedArrayBufferPrototype>();
static_cast<SharedArrayBufferPrototype *>(sharedArrayBufferPrototype())->init(this, sharedArrayBufferCtor());
- jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global);
+ jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(this);
jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>();
static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor());
- jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global);
+ jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(this);
jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>();
static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor());
jsObjects[ValueTypeProto] = (Heap::Base *) nullptr;
jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr;
+ jsObjects[TypeWrapperProto] = (Heap::Base *) nullptr;
- jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global);
+ jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(this);
jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>();
static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype())
->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor()));
for (int i = 0; i < NTypedArrayTypes; ++i) {
- static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
+ static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(this, Heap::TypedArray::Type(i));
static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i));
typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>()));
}
@@ -586,6 +777,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("Promise"), *promiseCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("URL"), *urlCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("URLSearchParams"), *urlSearchParamsCtor());
globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
@@ -602,14 +795,14 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>()));
globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>()));
globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>()));
- globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext())));
+ globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(this)));
globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Value::undefinedValue());
globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN()));
globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY));
- jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global);
+ jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(this);
globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction());
// ES6: 20.1.2.12 & 20.1.2.13:
@@ -638,9 +831,11 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
- ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError));
+ ScopedFunctionObject t(
+ scope,
+ memoryManager->allocate<DynamicFunctionObject>(this, 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);
@@ -648,18 +843,31 @@ 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);
+
+ QV4::QObjectWrapper::initializeBindings(this);
+
+ m_delayedCallQueue.init(this);
+ isInitialized = true;
}
ExecutionEngine::~ExecutionEngine()
{
- modules.clear();
+ for (auto val : nativeModules) {
+ PersistentValueStorage::free(val);
+ }
+ nativeModules.clear();
+ qDeleteAll(m_extensionData);
delete m_multiplyWrappedQObjects;
m_multiplyWrappedQObjects = nullptr;
delete identifierTable;
delete memoryManager;
- while (!compilationUnits.isEmpty())
- (*compilationUnits.begin())->unlink();
+ for (const auto &cu : std::as_const(m_compilationUnits)) {
+ Q_ASSERT(cu->engine == this);
+ cu->clear();
+ cu->engine = nullptr;
+ }
+ m_compilationUnits.clear();
delete bumperPointerAllocator;
delete regExpCache;
@@ -669,11 +877,11 @@ ExecutionEngine::~ExecutionEngine()
delete jsStack;
gcStack->deallocate();
delete gcStack;
-}
-ExecutionContext *ExecutionEngine::currentContext() const
-{
- return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
+#if QT_CONFIG(qml_xml_http_request)
+ qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
+ m_xmlHttpRequestData = nullptr;
+#endif
}
#if QT_CONFIG(qml_debug)
@@ -693,7 +901,7 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
void ExecutionEngine::initRootContext()
{
Scope scope(this);
- Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data)));
+ Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>());
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
@@ -727,13 +935,13 @@ Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
Heap::String *ExecutionEngine::newString(const QString &s)
{
- return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s);
+ return memoryManager->allocWithStringData<String>(s.size() * sizeof(QChar), s);
}
Heap::String *ExecutionEngine::newIdentifier(const QString &text)
{
Scope scope(this);
- ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text));
+ ScopedString s(scope, memoryManager->allocWithStringData<String>(text.size() * sizeof(QChar), text));
s->toPropertyKey();
return s->d();
}
@@ -813,24 +1021,35 @@ Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length)
return memoryManager->allocate<ArrayBuffer>(length);
}
+Heap::DateObject *ExecutionEngine::newDateObject(double dateTime)
+{
+ return memoryManager->allocate<DateObject>(dateTime);
+}
-Heap::DateObject *ExecutionEngine::newDateObject(const Value &value)
+Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dateTime)
{
- return memoryManager->allocate<DateObject>(value);
+ return memoryManager->allocate<DateObject>(dateTime);
}
-Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
+Heap::DateObject *ExecutionEngine::newDateObject(
+ QDate date, Heap::Object *parent, int index, uint flags)
{
- Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt));
- return object->d();
+ return memoryManager->allocate<DateObject>(
+ date, parent, index, Heap::ReferenceObject::Flags(flags));
}
-Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
+Heap::DateObject *ExecutionEngine::newDateObject(
+ QTime time, Heap::Object *parent, int index, uint flags)
{
- Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t));
- return object->d();
+ return memoryManager->allocate<DateObject>(
+ time, parent, index, Heap::ReferenceObject::Flags(flags));
+}
+
+Heap::DateObject *ExecutionEngine::newDateObject(
+ QDateTime dateTime, Heap::Object *parent, int index, uint flags)
+{
+ return memoryManager->allocate<DateObject>(
+ dateTime, parent, index, Heap::ReferenceObject::Flags(flags));
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
@@ -845,10 +1064,30 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
return memoryManager->allocate<RegExpObject>(re);
}
-Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
+#if QT_CONFIG(regularexpression)
+Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re)
{
return memoryManager->allocate<RegExpObject>(re);
}
+#endif
+
+Heap::UrlObject *ExecutionEngine::newUrlObject()
+{
+ return memoryManager->allocate<UrlObject>();
+}
+
+Heap::UrlObject *ExecutionEngine::newUrlObject(const QUrl &url)
+{
+ Scope scope(this);
+ Scoped<UrlObject> urlObject(scope, newUrlObject());
+ urlObject->setUrl(url);
+ return urlObject->d();
+}
+
+Heap::UrlSearchParamsObject *ExecutionEngine::newUrlSearchParamsObject()
+{
+ return memoryManager->allocate<UrlSearchParamsObject>();
+}
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
{
@@ -939,9 +1178,9 @@ Heap::Object *ExecutionEngine::newEvalErrorObject(const QString &message)
return ErrorObject::create<EvalErrorObject>(this, message);
}
-Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v)
+Heap::Object *ExecutionEngine::newVariantObject(const QMetaType type, const void *data)
{
- return memoryManager->allocate<VariantObject>(v);
+ return memoryManager->allocate<VariantObject>(type, data);
}
Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o)
@@ -968,21 +1207,9 @@ Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o)
Heap::QmlContext *ExecutionEngine::qmlContext() const
{
- if (!currentStackFrame)
- return nullptr;
- Heap::ExecutionContext *ctx = currentContext()->d();
-
- if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !ctx->outer)
- return nullptr;
-
- while (ctx->outer && ctx->outer->type != Heap::ExecutionContext::Type_GlobalContext)
- ctx = ctx->outer;
-
- Q_ASSERT(ctx);
- if (ctx->type != Heap::ExecutionContext::Type_QmlContext)
- return nullptr;
-
- return static_cast<Heap::QmlContext *>(ctx);
+ return currentStackFrame
+ ? static_cast<Heap::QmlContext *>(qmlContext(currentContext()->d()))
+ : nullptr;
}
QObject *ExecutionEngine::qmlScopeObject() const
@@ -994,19 +1221,17 @@ QObject *ExecutionEngine::qmlScopeObject() const
return ctx->qml()->scopeObject;
}
-QQmlContextData *ExecutionEngine::callingQmlContext() const
+QQmlRefPointer<QQmlContextData> ExecutionEngine::callingQmlContext() const
{
Heap::QmlContext *ctx = qmlContext();
if (!ctx)
return nullptr;
- return ctx->qml()->context->contextData();
+ return ctx->qml()->context;
}
StackTrace ExecutionEngine::stackTrace(int frameLimit) const
{
- Scope scope(const_cast<ExecutionEngine *>(this));
- ScopedString name(scope);
StackTrace stack;
CppStackFrame *f = currentStackFrame;
@@ -1014,16 +1239,18 @@ StackTrace ExecutionEngine::stackTrace(int frameLimit) const
QV4::StackFrame frame;
frame.source = f->source();
frame.function = f->function();
- frame.line = qAbs(f->lineNumber());
- frame.column = -1;
+ frame.line = f->lineNumber();
+
stack.append(frame);
- if (f->isTailCalling) {
- QV4::StackFrame frame;
- frame.function = QStringLiteral("[elided tail calls]");
- stack.append(frame);
+ if (f->isJSTypesFrame()) {
+ if (static_cast<JSTypesStackFrame *>(f)->isTailCalling()) {
+ QV4::StackFrame frame;
+ frame.function = QStringLiteral("[elided tail calls]");
+ stack.append(frame);
+ }
}
--frameLimit;
- f = f->parent;
+ f = f->parentFrame();
}
return stack;
@@ -1050,7 +1277,7 @@ static inline char *v4StackTrace(const ExecutionContext *context)
const QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toString();
str << "frame={level=\"" << i << "\",func=\"" << stackTrace.at(i).function
<< "\",file=\"" << fileName << "\",fullname=\"" << fileName
- << "\",line=\"" << stackTrace.at(i).line << "\",language=\"js\"}";
+ << "\",line=\"" << qAbs(stackTrace.at(i).line) << "\",language=\"js\"}";
}
}
str << ']';
@@ -1062,6 +1289,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);
@@ -1075,7 +1308,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file)
base = f->v4Function->finalUrl();
break;
}
- f = f->parent;
+ f = f->parentFrame();
}
if (base.isEmpty() && globalCode)
@@ -1089,17 +1322,15 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file)
void ExecutionEngine::markObjects(MarkStack *markStack)
{
- for (int i = 0; i < NClasses; ++i)
- if (classes[i])
- classes[i]->mark(markStack);
- markStack->drain();
+ for (int i = 0; i < NClasses; ++i) {
+ if (Heap::InternalClass *c = classes[i])
+ c->mark(markStack);
+ }
identifierTable->markObjects(markStack);
- for (auto compilationUnit: compilationUnits) {
+ for (const auto &compilationUnit : std::as_const(m_compilationUnits))
compilationUnit->markObjects(markStack);
- markStack->drain();
- }
}
ReturnedValue ExecutionEngine::throwError(const Value &value)
@@ -1241,7 +1472,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
if (!trace.isEmpty()) {
QV4::StackFrame frame = trace.constFirst();
error.setUrl(QUrl(frame.source));
- error.setLine(frame.line);
+ error.setLine(qAbs(frame.line));
error.setColumn(frame.column);
}
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
@@ -1252,50 +1483,51 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
// Variant conversion code
typedef QSet<QV4::Heap::Object *> V4ObjectSet;
-static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects);
-static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value);
-static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr);
-static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value,
- const QByteArray &targetType,
- void **result);
-static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst);
+enum class JSToQVariantConversionBehavior {Never, Safish, Aggressive };
+static QVariant toVariant(
+ const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior,
+ V4ObjectSet *visitedObjects);
+static QObject *qtObjectFromJS(const QV4::Value &value);
+static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr,
+ JSToQVariantConversionBehavior behavior = JSToQVariantConversionBehavior::Safish);
+static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap);
static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value)
{
- return v4->metaTypeToJS(value.userType(), value.constData());
+ return v4->metaTypeToJS(value.metaType(), value.constData());
}
-
-QVariant ExecutionEngine::toVariant(const Value &value, int typeHint, bool createJSValueForObjects)
-{
- return ::toVariant(this, value, typeHint, createJSValueForObjects, nullptr);
-}
-
-
-static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects)
+static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVariantConversionBehavior conversionBehavior,
+ V4ObjectSet *visitedObjects)
{
Q_ASSERT (!value.isEmpty());
- QV4::Scope scope(e);
if (const QV4::VariantObject *v = value.as<QV4::VariantObject>())
return v->d()->data();
- if (typeHint == QVariant::Bool)
+ if (metaType == QMetaType::fromType<bool>())
return QVariant(value.toBoolean());
- if (typeHint == QMetaType::QJsonValue)
+ if (metaType == QMetaType::fromType<double>())
+ return QVariant(value.toNumber());
+
+ if (metaType == QMetaType::fromType<float>())
+ return QVariant(float(value.toNumber()));
+
+ if (metaType == QMetaType::fromType<QJsonValue>())
return QVariant::fromValue(QV4::JsonObject::toJsonValue(value));
- if (typeHint == qMetaTypeId<QJSValue>())
- return QVariant::fromValue(QJSValue(e, value.asReturnedValue()));
+ if (metaType == QMetaType::fromType<QJSValue>())
+ return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(value.asReturnedValue()));
- if (value.as<QV4::Object>()) {
- QV4::ScopedObject object(scope, value);
- if (typeHint == QMetaType::QJsonObject
+ if (const QV4::Object *o = value.as<QV4::Object>()) {
+ QV4::Scope scope(o->engine());
+ QV4::ScopedObject object(scope, o);
+ if (metaType == QMetaType::fromType<QJsonObject>()
&& !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>()) {
@@ -1304,16 +1536,15 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return v->toVariant();
} else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) {
return l->toVariant();
-#if QT_CONFIG(qml_sequence_object)
- } else if (object->isListType()) {
- return QV4::SequencePrototype::toVariant(object);
-#endif
+ } else if (QV4::Sequence *s = object->as<QV4::Sequence>()) {
+ return QV4::SequencePrototype::toVariant(s);
}
}
- if (value.as<ArrayObject>()) {
- QV4::ScopedArrayObject a(scope, value);
- if (typeHint == qMetaTypeId<QList<QObject *> >()) {
+ if (const QV4::ArrayObject *o = value.as<ArrayObject>()) {
+ QV4::Scope scope(o->engine());
+ QV4::ScopedArrayObject a(scope, o);
+ if (metaType == QMetaType::fromType<QList<QObject *>>()) {
QList<QObject *> list;
uint length = a->getLength();
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
@@ -1326,17 +1557,65 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
}
}
- return qVariantFromValue<QList<QObject*> >(list);
- } else if (typeHint == QMetaType::QJsonArray) {
+ return QVariant::fromValue<QList<QObject*> >(list);
+ } else if (metaType == QMetaType::fromType<QJsonArray>()) {
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
-#if QT_CONFIG(qml_sequence_object)
- bool succeeded = false;
- QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded);
- if (succeeded)
+ QVariant retn = QV4::SequencePrototype::toVariant(value, metaType);
+ if (retn.isValid())
return retn;
-#endif
+
+ if (metaType.isValid()) {
+ retn = QVariant(metaType, nullptr);
+ auto retnAsIterable = retn.value<QSequentialIterable>();
+ if (retnAsIterable.metaContainer().canAddValue()) {
+ QMetaType valueMetaType = retnAsIterable.metaContainer().valueMetaType();
+ auto const length = a->getLength();
+ QV4::ScopedValue arrayValue(scope);
+ for (qint64 i = 0; i < length; ++i) {
+ arrayValue = a->get(i);
+ QVariant asVariant = QQmlValueTypeProvider::createValueType(
+ arrayValue, valueMetaType);
+ if (asVariant.isValid()) {
+ retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
+ continue;
+ }
+
+ if (QMetaType::canConvert(QMetaType::fromType<QJSValue>(), valueMetaType)) {
+ // before attempting a conversion from the concrete types,
+ // check if there exists a conversion from QJSValue -> out type
+ // prefer that one for compatibility reasons
+ asVariant = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(
+ arrayValue->asReturnedValue()));
+ if (asVariant.convert(valueMetaType)) {
+ retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
+ continue;
+ }
+ }
+
+ asVariant = toVariant(arrayValue, valueMetaType, JSToQVariantConversionBehavior::Never, visitedObjects);
+ if (valueMetaType == QMetaType::fromType<QVariant>()) {
+ retnAsIterable.metaContainer().addValue(retn.data(), &asVariant);
+ } else {
+ auto originalType = asVariant.metaType();
+ bool couldConvert = asVariant.convert(valueMetaType);
+ if (!couldConvert && originalType.isValid()) {
+ // If the original type was void, we're converting a "hole" in a sparse
+ // array. There is no point in warning about that.
+ qWarning().noquote()
+ << QLatin1String("Could not convert array value "
+ "at position %1 from %2 to %3")
+ .arg(QString::number(i),
+ QString::fromUtf8(originalType.name()),
+ QString::fromUtf8(valueMetaType.name()));
+ }
+ retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
+ }
+ }
+ return retn;
+ }
+ }
}
if (value.isUndefined())
@@ -1352,33 +1631,93 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
if (String *s = value.stringValue()) {
const QString &str = s->toQString();
// QChars are stored as a strings
- if (typeHint == QVariant::Char && str.size() == 1)
+ if (metaType == QMetaType::fromType<QChar>() && str.size() == 1)
return str.at(0);
return str;
}
-#if QT_CONFIG(qml_locale)
- if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
- return *ld->d()->locale;
-#endif
- if (const QV4::DateObject *d = value.as<DateObject>())
+ if (const QV4::DateObject *d = value.as<DateObject>()) {
+ // NOTE: since we convert QTime to JS Date,
+ // round trip will change the variant type (to QDateTime)!
+
+ if (metaType == QMetaType::fromType<QDate>())
+ return DateObject::dateTimeToDate(d->toQDateTime());
+
+ if (metaType == QMetaType::fromType<QTime>())
+ return d->toQDateTime().time();
+
+ if (metaType == QMetaType::fromType<QString>())
+ return d->toString();
+
return d->toQDateTime();
+ }
+ if (const QV4::UrlObject *d = value.as<UrlObject>())
+ return d->toQUrl();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
return d->asByteArray();
- // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
+ if (const Symbol *symbol = value.as<Symbol>()) {
+ return conversionBehavior == JSToQVariantConversionBehavior::Never
+ ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue()))
+ : symbol->descriptiveString();
+ }
- QV4::ScopedObject o(scope, value);
- Q_ASSERT(o);
+ const QV4::Object *object = value.as<QV4::Object>();
+ Q_ASSERT(object);
+ QV4::Scope scope(object->engine());
+ QV4::ScopedObject o(scope, object);
+#if QT_CONFIG(regularexpression)
if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>())
- return re->toQRegExp();
+ return re->toQRegularExpression();
+#endif
+
+ if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) {
+ const QVariant result = QQmlValueTypeProvider::createValueType(value, metaType);
+ if (result.isValid())
+ return result;
+ }
- if (createJSValueForObjects)
- return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue()));
+ if (conversionBehavior == JSToQVariantConversionBehavior::Never)
+ return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
- return objectToVariant(e, o, visitedObjects);
+ return objectToVariant(o, visitedObjects, conversionBehavior);
}
-static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects)
+QVariant ExecutionEngine::toVariantLossy(const Value &value)
+{
+ return ::toVariant(value, QMetaType(), JSToQVariantConversionBehavior::Aggressive, nullptr);
+}
+
+QVariant ExecutionEngine::toVariant(
+ const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols)
+{
+ auto behavior = createJSValueForObjectsAndSymbols ? JSToQVariantConversionBehavior::Never
+ : JSToQVariantConversionBehavior::Safish;
+ return ::toVariant(value, typeHint, behavior, nullptr);
+}
+
+static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visitedObjects,
+ JSToQVariantConversionBehavior conversionBehvior)
+{
+ QVariantMap map;
+ QV4::Scope scope(o->engine());
+ QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
+ QV4::ScopedValue name(scope);
+ QV4::ScopedValue val(scope);
+ while (1) {
+ name = it.nextPropertyNameAsString(val);
+ if (name->isNull())
+ break;
+
+ QString key = name->toQStringNoThrow();
+ map.insert(key, ::toVariant(
+ val, /*type hint*/ QMetaType {},
+ conversionBehvior, visitedObjects));
+ }
+ return map;
+}
+
+static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects,
+ JSToQVariantConversionBehavior conversionBehvior)
{
Q_ASSERT(o);
@@ -1398,7 +1737,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
QVariant result;
if (o->as<ArrayObject>()) {
- QV4::Scope scope(e);
+ QV4::Scope scope(o->engine());
QV4::ScopedArrayObject a(scope, o->asReturnedValue());
QV4::ScopedValue v(scope);
QVariantList list;
@@ -1406,66 +1745,51 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
v = a->get(ii);
- list << ::toVariant(e, v, -1, /*createJSValueForObjects*/false, visitedObjects);
+ list << ::toVariant(v, QMetaType {}, conversionBehvior,
+ visitedObjects);
}
result = list;
- } else if (!o->as<FunctionObject>()) {
- QVariantMap map;
- QV4::Scope scope(e);
- QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
- QV4::ScopedValue name(scope);
- QV4::ScopedValue val(scope);
- while (1) {
- name = it.nextPropertyNameAsString(val);
- if (name->isNull())
- break;
-
- QString key = name->toQStringNoThrow();
- map.insert(key, ::toVariant(e, val, /*type hint*/-1, /*createJSValueForObjects*/false, visitedObjects));
- }
-
- result = map;
+ } else if (o->getPrototypeOf() == o->engine()->objectPrototype()->d()
+ || (conversionBehvior == JSToQVariantConversionBehavior::Aggressive &&
+ !o->as<QV4::FunctionObject>())) {
+ /* FunctionObject is excluded for historical reasons, even though
+ objects with a custom prototype risk losing information
+ But the Aggressive path is used only in QJSValue::toVariant
+ which is documented to be lossy
+ */
+ result = objectToVariantMap(o, visitedObjects, conversionBehvior);
+ } else {
+ // If it's not a plain object, we can only save it as QJSValue.
+ result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
}
visitedObjects->remove(o->d());
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);
+/*!
+ \internal
-QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
+ Transform the given \a metaType and \a ptr into a JavaScript representation.
+ */
+QV4::ReturnedValue ExecutionEngine::fromData(
+ QMetaType metaType, const void *ptr,
+ QV4::Heap::Object *container, int property, uint flags)
{
- int type = variant.userType();
- const void *ptr = variant.constData();
+ const auto createSequence = [&](const QMetaSequence metaSequence) {
+ QV4::Scope scope(this);
+ QV4::Scoped<Sequence> sequence(scope);
+ if (container) {
+ return QV4::SequencePrototype::newSequence(
+ this, metaType, metaSequence, ptr,
+ container, property, Heap::ReferenceObject::Flags(flags));
+ } else {
+ return QV4::SequencePrototype::fromData(this, metaType, metaSequence, ptr);
+ }
+ };
+ const int type = metaType.id();
if (type < QMetaType::User) {
switch (QMetaType::Type(type)) {
case QMetaType::UnknownType:
@@ -1480,6 +1804,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(*reinterpret_cast<const int*>(ptr));
case QMetaType::UInt:
return QV4::Encode(*reinterpret_cast<const uint*>(ptr));
+ case QMetaType::Long:
+ return QV4::Encode((double)*reinterpret_cast<const long *>(ptr));
+ case QMetaType::ULong:
+ return QV4::Encode((double)*reinterpret_cast<const ulong *>(ptr));
case QMetaType::LongLong:
return QV4::Encode((double)*reinterpret_cast<const qlonglong*>(ptr));
case QMetaType::ULongLong:
@@ -1500,124 +1828,159 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode((int)*reinterpret_cast<const char*>(ptr));
case QMetaType::UChar:
return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(ptr));
+ case QMetaType::SChar:
+ return QV4::Encode((int)*reinterpret_cast<const signed char*>(ptr));
case QMetaType::QChar:
return newString(*reinterpret_cast<const QChar *>(ptr))->asReturnedValue();
+ case QMetaType::Char16:
+ return newString(QChar(*reinterpret_cast<const char16_t *>(ptr)))->asReturnedValue();
case QMetaType::QDateTime:
- return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(ptr)));
+ return QV4::Encode(newDateObject(
+ *reinterpret_cast<const QDateTime *>(ptr),
+ container, property, flags));
case QMetaType::QDate:
- return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(ptr), QTime(0, 0, 0), Qt::UTC)));
+ return QV4::Encode(newDateObject(
+ *reinterpret_cast<const QDate *>(ptr),
+ container, property, flags));
case QMetaType::QTime:
- return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr)));
- case QMetaType::QRegExp:
- return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
+ return QV4::Encode(newDateObject(
+ *reinterpret_cast<const QTime *>(ptr),
+ container, property, flags));
+#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)
case QMetaType::QStringList:
- {
- bool succeeded = false;
- QV4::Scope scope(this);
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded));
- if (succeeded)
- return retn->asReturnedValue();
- return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr)));
- }
-#endif
+ return createSequence(QMetaSequence::fromContainer<QStringList>());
case QMetaType::QVariantList:
- return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
+ return createSequence(QMetaSequence::fromContainer<QVariantList>());
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:
return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
case QMetaType::QJsonArray:
return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
-#if QT_CONFIG(qml_locale)
- case QMetaType::QLocale:
- return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
-#endif
case QMetaType::QPixmap:
case QMetaType::QImage:
// Scarce value types
- return QV4::Encode(newVariantObject(variant));
+ return QV4::Encode(newVariantObject(metaType, ptr));
default:
break;
}
+ }
- if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type))
- return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type);
- } else {
- QV4::Scope scope(this);
- if (type == qMetaTypeId<QQmlListReference>()) {
- typedef QQmlListReferencePrivate QDLRP;
- QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr));
- if (p->object) {
- return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType);
- } else {
- return QV4::Encode::null();
- }
- } else if (type == qMetaTypeId<QJSValue>()) {
- const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr);
- return QJSValuePrivate::convertedToValue(this, *value);
- } else if (type == qMetaTypeId<QList<QObject *> >()) {
- // XXX Can this be made more by using Array as a prototype and implementing
- // directly against QList<QObject*>?
- const QList<QObject *> &list = *(const QList<QObject *>*)ptr;
- QV4::ScopedArrayObject a(scope, newArrayObject());
- a->arrayReserve(list.count());
- QV4::ScopedValue v(scope);
- for (int ii = 0; ii < list.count(); ++ii)
- a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii))));
- a->setArrayLengthUnchecked(list.count());
- return a.asReturnedValue();
- } else if (QMetaType::typeFlags(type) & QMetaType::PointerToQObject) {
+ if (metaType.flags() & QMetaType::IsEnumeration)
+ return fromData(metaType.underlyingType(), ptr, container, property, flags);
+
+ QV4::Scope scope(this);
+ if (metaType == QMetaType::fromType<QQmlListReference>()) {
+ typedef QQmlListReferencePrivate QDLRP;
+ QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr));
+ if (p->object)
+ return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType);
+ else
+ return QV4::Encode::null();
+ } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) {
+ // casting to QQmlListProperty<QObject> is slightly nasty, but it's the
+ // same QQmlListReference does.
+ const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr);
+ if (p->object)
+ return QV4::QmlListWrapper::create(scope.engine, *p, metaType);
+ else
+ return QV4::Encode::null();
+ } else if (metaType == QMetaType::fromType<QJSValue>()) {
+ return QJSValuePrivate::convertToReturnedValue(
+ this, *reinterpret_cast<const QJSValue *>(ptr));
+ } else if (metaType == QMetaType::fromType<QList<QObject *> >()) {
+ // XXX Can this be made more by using Array as a prototype and implementing
+ // directly against QList<QObject*>?
+ const QList<QObject *> &list = *(const QList<QObject *>*)ptr;
+ QV4::ScopedArrayObject a(scope, newArrayObject());
+ a->arrayReserve(list.size());
+ QV4::ScopedValue v(scope);
+ for (int ii = 0; ii < list.size(); ++ii)
+ a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii))));
+ a->setArrayLengthUnchecked(list.size());
+ return a.asReturnedValue();
+ } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
+ if (flags.testFlag(QMetaType::IsConst))
+ return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr));
+ else
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
+ } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr);
+ switch (primitive->type()) {
+ case QJSPrimitiveValue::Boolean:
+ return Encode(primitive->asBoolean());
+ case QJSPrimitiveValue::Integer:
+ return Encode(primitive->asInteger());
+ case QJSPrimitiveValue::String:
+ return newString(primitive->asString())->asReturnedValue();
+ case QJSPrimitiveValue::Undefined:
+ return Encode::undefined();
+ case QJSPrimitiveValue::Null:
+ return Encode::null();
+ case QJSPrimitiveValue::Double:
+ return Encode(primitive->asDouble());
}
+ }
- bool objOk;
- QObject *obj = QQmlMetaType::toQObject(variant, &objOk);
- if (objOk)
- return QV4::QObjectWrapper::wrap(this, obj);
+ if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) {
+ if (container) {
+ return QV4::QQmlValueTypeWrapper::create(
+ this, ptr, vtmo, metaType,
+ container, property, Heap::ReferenceObject::Flags(flags));
+ } else {
+ return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
+ }
+ }
-#if QT_CONFIG(qml_sequence_object)
- bool succeeded = false;
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded));
- if (succeeded)
- return retn->asReturnedValue();
-#endif
+ const QQmlType listType = QQmlMetaType::qmlListType(metaType);
+ if (listType.isSequentialContainer())
+ return createSequence(listType.listMetaSequence());
- if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type))
- return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type);
- }
+ QSequentialIterable iterable;
+ if (QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &iterable)) {
- // XXX TODO: To be compatible, we still need to handle:
- // + QObjectList
- // + QList<int>
+ // If the resulting iterable is useful for anything, turn it into a QV4::Sequence.
+ const QMetaSequence sequence = iterable.metaContainer();
+ if (sequence.hasSize() && sequence.canGetValueAtIndex())
+ return createSequence(sequence);
- return QV4::Encode(newVariantObject(variant));
+ // As a last resort, try to read the contents of the container via an iterator
+ // and build a JS array from them.
+ if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
+ QV4::ScopedArrayObject a(scope, newArrayObject());
+ for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
+ a->push_back(fromVariant(*it));
+ return a.asReturnedValue();
+ }
+ }
+
+ return QV4::Encode(newVariantObject(metaType, ptr));
}
-QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
+QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
{
- return objectToVariant(this, o).toMap();
+ return fromData(variant.metaType(), variant.constData());
}
+ReturnedValue ExecutionEngine::fromVariant(
+ const QVariant &variant, Heap::Object *parent, int property, uint flags)
+{
+ return fromData(variant.metaType(), variant.constData(), parent, property, flags);
+}
-// Converts a QVariantList to JS.
-// The result is a new Array object with length equal to the length
-// of the QVariantList, and the elements being the QVariantList's
-// elements converted to JS, recursively.
-static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst)
+QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
{
- QV4::Scope scope(v4);
- QV4::ScopedArrayObject a(scope, v4->newArrayObject());
- a->arrayReserve(lst.size());
- QV4::ScopedValue v(scope);
- for (int i = 0; i < lst.size(); i++)
- a->arrayPut(i, (v = variantToJS(v4, lst.at(i))));
- a->setArrayLengthUnchecked(lst.size());
- return a.asReturnedValue();
+ Q_ASSERT(o);
+ V4ObjectSet visitedObjects;
+ visitedObjects.insert(o->d());
+ return objectToVariantMap(o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
}
// Converts a QVariantMap to JS.
@@ -1635,9 +1998,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);
}
@@ -1646,94 +2008,49 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian
// Converts the meta-type defined by the given type and data to JS.
// Returns the value if conversion succeeded, an empty handle otherwise.
-QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
+QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType 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:
- 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 (mt.flags() & QMetaType::IsGadget) {
- Q_ASSERT(mt.metaObject());
- return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), mt.metaObject(), type);
- }
- // Fall back to wrapping in a QVariant.
- return QV4::Encode(newVariantObject(QVariant(type, data)));
- }
+ if (type == QMetaType::fromType<QVariant>()) {
+ // unwrap it: this is tested in QJSEngine, and makes the most sense for
+ // end-user code too.
+ return fromVariant(*reinterpret_cast<const QVariant*>(data));
+ } else if (type == QMetaType::fromType<QUrl>()) {
+ // Create a proper URL object here, rather than a variant.
+ return newUrlObject(*reinterpret_cast<const QUrl *>(data))->asReturnedValue();
+ }
+
+ return fromData(type, data);
+}
+
+int ExecutionEngine::maxJSStackSize() const
+{
+ return s_maxJSStackSize;
+}
+
+int ExecutionEngine::maxGCStackSize() const
+{
+ return s_maxGCStackSize;
+}
+
+/*!
+ \internal
+ Returns \a length converted to int if its safe to
+ pass to \c Scope::alloc.
+ Otherwise it throws a RangeError, and returns 0.
+ */
+int ExecutionEngine::safeForAllocLength(qint64 len64)
+{
+ if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) {
+ throwRangeError(QStringLiteral("Invalid array length."));
+ return 0;
+ }
+ if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) {
+ throwRangeError(QStringLiteral("Array too large for apply()."));
+ return 0;
}
- Q_UNREACHABLE();
- return 0;
+ return len64;
}
ReturnedValue ExecutionEngine::global()
@@ -1741,8 +2058,24 @@ 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;
+ const DiskCacheOptions options = diskCacheOptions();
+ if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (options & DiskCache::Aot)
+ ? QQmlMetaType::findCachedCompilationUnit(
+ url,
+ (options & DiskCache::AotByteCode)
+ ? QQmlMetaType::AcceptUntyped
+ : QQmlMetaType::RequireFullyTyped,
+ &cacheError)
+ : nullptr) {
+ return executableCompilationUnit(
+ QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
+ cachedUnit->qmlData, cachedUnit->aotCompiledFunctions, url.fileName(),
+ url.toString()));
+ }
+
QFile f(QQmlFile::urlToLocalFileOrQrc(url));
if (!f.open(QIODevice::ReadOnly)) {
throwError(QStringLiteral("Could not open module %1 for reading").arg(url.toString()));
@@ -1758,10 +2091,12 @@ 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);
@@ -1771,182 +2106,524 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(con
<< ": warning: " << m.message;
}
}
- return unit;
+
+ return insertCompilationUnit(std::move(unit));
}
-#endif // ifndef V4_BOOTSTRAP
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compilationUnitForUrl(const QUrl &url) const
+{
+ // Gives the _most recently inserted_ CU of that URL. That's what we want.
+ return m_compilationUnits.value(url);
+}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(bool debugMode, const QString &url, const QString &sourceCode,
- const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics)
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::executableCompilationUnit(
+ QQmlRefPointer<CompiledData::CompilationUnit> &&unit)
{
- QQmlJS::Engine ee;
- QQmlJS::Lexer lexer(&ee);
- lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false);
- QQmlJS::Parser parser(&ee);
+ const QUrl url = unit->finalUrl();
+ auto [begin, end] = std::as_const(m_compilationUnits).equal_range(url);
- const bool parsed = parser.parseModule();
+ for (auto it = begin; it != end; ++it) {
+ if ((*it)->baseCompilationUnit() == unit)
+ return *it;
+ }
- if (diagnostics)
- *diagnostics = parser.diagnosticMessages();
+ auto executableUnit = m_compilationUnits.insert(
+ url, ExecutableCompilationUnit::create(std::move(unit), this));
+ // runtime data should not be initialized yet, so we don't need to mark the CU
+ Q_ASSERT(!(*executableUnit)->runtimeStrings);
+ return *executableUnit;
+}
- if (!parsed)
- return nullptr;
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::insertCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&unit) {
+ QUrl url = unit->finalUrl();
+ auto executableUnit = ExecutableCompilationUnit::create(std::move(unit), this);
+ /* Compilation Units stored in the engine are part of the gc roots,
+ so we don't trigger any write-barrier when they are added. Use
+ markCustom to make sure they are still marked when we insert them */
+ QV4::WriteBarrier::markCustom(this, [&executableUnit](QV4::MarkStack *ms) {
+ executableUnit->markObjects(ms);
+ });
+ return *m_compilationUnits.insert(std::move(url), std::move(executableUnit));
+}
- 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;
+void ExecutionEngine::trimCompilationUnits()
+{
+ for (auto it = m_compilationUnits.begin(); it != m_compilationUnits.end();) {
+ if ((*it)->count() == 1)
+ it = m_compilationUnits.erase(it);
+ else
+ ++it;
}
+}
+
+ExecutionEngine::Module ExecutionEngine::moduleForUrl(
+ const QUrl &url, const ExecutableCompilationUnit *referrer) const
+{
+ const auto nativeModule = nativeModules.find(url);
+ if (nativeModule != nativeModules.end())
+ return Module { nullptr, *nativeModule };
+
+ const QUrl resolved = referrer
+ ? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
+ : QQmlTypeLoader::normalize(url);
+ auto existingModule = m_compilationUnits.find(resolved);
+ if (existingModule == m_compilationUnits.end())
+ return Module { nullptr, nullptr };
+ return Module { *existingModule, nullptr };
+}
+
+ExecutionEngine::Module ExecutionEngine::loadModule(const QUrl &url, const ExecutableCompilationUnit *referrer)
+{
+ const auto nativeModule = nativeModules.constFind(url);
+ if (nativeModule != nativeModules.cend())
+ return Module { nullptr, *nativeModule };
- 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())
+ const QUrl resolved = referrer
+ ? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
+ : QQmlTypeLoader::normalize(url);
+ auto existingModule = m_compilationUnits.constFind(resolved);
+ if (existingModule != m_compilationUnits.cend())
+ return Module { *existingModule, nullptr };
+
+ auto newModule = compileModule(resolved);
+ Q_ASSERT(!newModule || m_compilationUnits.contains(resolved, newModule));
+
+ return Module { newModule, nullptr };
+}
+
+QV4::Value *ExecutionEngine::registerNativeModule(const QUrl &url, const QV4::Value &module)
+{
+ const auto existingModule = nativeModules.constFind(url);
+ if (existingModule != nativeModules.cend())
return nullptr;
- return cg.generateCompilationUnit();
+ QV4::Value *val = this->memoryManager->m_persistentValues->allocate();
+ *val = module.asReturnedValue();
+ nativeModules.insert(url, val);
+
+ // Make sure the type loader doesn't try to resolve the script anymore.
+ if (m_qmlEngine)
+ QQmlEnginePrivate::get(m_qmlEngine)->typeLoader.injectScript(url, *val);
+
+ return val;
}
-#ifndef V4_BOOTSTRAP
+static ExecutionEngine::DiskCacheOptions transFormDiskCache(const char *v)
+{
+ using DiskCache = ExecutionEngine::DiskCache;
+
+ if (v == nullptr)
+ return DiskCache::Enabled;
+
+ ExecutionEngine::DiskCacheOptions result = DiskCache::Disabled;
+ const QList<QByteArray> options = QByteArray(v).split(',');
+ for (const QByteArray &option : options) {
+ if (option == "aot-bytecode")
+ result |= DiskCache::AotByteCode;
+ else if (option == "aot-native")
+ result |= DiskCache::AotNative;
+ else if (option == "aot")
+ result |= DiskCache::Aot;
+ else if (option == "qmlc-read")
+ result |= DiskCache::QmlcRead;
+ else if (option == "qmlc-write")
+ result |= DiskCache::QmlcWrite;
+ else if (option == "qmlc")
+ result |= DiskCache::Qmlc;
+ else
+ qWarning() << "Ignoring unknown option to QML_DISK_CACHE:" << option;
+ }
+
+ return result;
+}
-void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit)
+ExecutionEngine::DiskCacheOptions ExecutionEngine::diskCacheOptions() const
{
- // Injection can happen from the QML type loader thread for example, but instantiation and
- // evaluation must be limited to the ExecutionEngine's thread.
- QMutexLocker moduleGuard(&moduleMutex);
- modules.insert(moduleUnit->finalUrl(), moduleUnit);
+ if (forceDiskCache())
+ return DiskCache::Enabled;
+ if (disableDiskCache() || debugger())
+ return DiskCache::Disabled;
+ static const DiskCacheOptions options = qmlGetConfigOption<
+ DiskCacheOptions, transFormDiskCache>("QML_DISK_CACHE");
+ return options;
}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer) const
+void ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
+ QV4::ExecutionContext *context, int argc, void **args,
+ QMetaType *types)
{
- QUrl url = QQmlTypeLoader::normalize(_url);
- if (referrer)
- url = referrer->finalUrl().resolved(url);
+ if (!args) {
+ Q_ASSERT(argc == 0);
+ void *dummyArgs[] = { nullptr };
+ QMetaType dummyTypes[] = { QMetaType::fromType<void>() };
+ function->call(self, dummyArgs, dummyTypes, argc, context);
+ return;
+ }
+ Q_ASSERT(types); // both args and types must be present
+ // implicitly sets the return value, which is args[0]
+ function->call(self, args, types, argc, context);
+}
- QMutexLocker moduleGuard(&moduleMutex);
- auto existingModule = modules.find(url);
- if (existingModule == modules.end())
- return nullptr;
- return *existingModule;
+QV4::ReturnedValue ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
+ QV4::ExecutionContext *context, int argc,
+ const QV4::Value *argv)
+{
+ QV4::Scope scope(this);
+ QV4::ScopedObject jsSelf(scope, QV4::QObjectWrapper::wrap(this, self));
+ Q_ASSERT(jsSelf);
+ return function->call(jsSelf, argv, argc, context);
+}
+
+void ExecutionEngine::initQmlGlobalObject()
+{
+ initializeGlobal();
+ lockObject(*globalObject);
+}
+
+void ExecutionEngine::initializeGlobal()
+{
+ createQtObject();
+
+ QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions);
+
+#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());
+ }
+ }
+ }
+}
+
+void ExecutionEngine::createQtObject()
+{
+ QV4::Scope scope(this);
+ QtObject *qtObject = new QtObject(this);
+ QJSEngine::setObjectOwnership(qtObject, QJSEngine::JavaScriptOwnership);
+
+ QV4::ScopedObject qtObjectWrapper(
+ scope, QV4::QObjectWrapper::wrap(this, qtObject));
+ QV4::ScopedObject qtNamespaceWrapper(
+ scope, QV4::QMetaObjectWrapper::create(this, &Qt::staticMetaObject));
+ QV4::ScopedObject qtObjectProtoWrapper(
+ scope, qtObjectWrapper->getPrototypeOf());
+
+ qtNamespaceWrapper->setPrototypeOf(qtObjectProtoWrapper);
+ qtObjectWrapper->setPrototypeOf(qtNamespaceWrapper);
+
+ globalObject->defineDefaultProperty(QStringLiteral("Qt"), qtObjectWrapper);
+}
+
+const QSet<QString> &ExecutionEngine::illegalNames() const
+{
+ return m_illegalNames;
}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer)
+void ExecutionEngine::setQmlEngine(QQmlEngine *engine)
{
- QUrl url = QQmlTypeLoader::normalize(_url);
- if (referrer)
- url = referrer->finalUrl().resolved(url);
+ // Second stage of initialization. We're updating some more prototypes here.
+ isInitialized = false;
+ m_qmlEngine = engine;
+ initQmlGlobalObject();
+ isInitialized = true;
+}
+
+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);
+ }
+}
- QMutexLocker moduleGuard(&moduleMutex);
- auto existingModule = modules.find(url);
- if (existingModule != modules.end())
- return *existingModule;
+void ExecutionEngine::freezeObject(const QV4::Value &value)
+{
+ QV4::Scope scope(this);
+ QV4::ScopedObject o(scope, value);
+ freeze_recursive(this, o);
+}
- moduleGuard.unlock();
+void ExecutionEngine::lockObject(const QV4::Value &value)
+{
+ QV4::Scope scope(this);
+ ScopedObject object(scope, value);
+ if (!object)
+ return;
+
+ std::vector<Heap::Object *> stack { object->d() };
+
+ // Methods meant to be overridden
+ const PropertyKey writableMembers[] = {
+ id_toString()->propertyKey(),
+ id_toLocaleString()->propertyKey(),
+ id_valueOf()->propertyKey(),
+ id_constructor()->propertyKey()
+ };
+ const auto writableBegin = std::begin(writableMembers);
+ const auto writableEnd = std::end(writableMembers);
+
+ while (!stack.empty()) {
+ object = stack.back();
+ stack.pop_back();
+
+ if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isLocked())
+ continue;
+
+ Scoped<InternalClass> locked(scope, object->internalClass()->locked());
+ QV4::ScopedObject member(scope);
+
+ // Taking this copy is cheap. It's refcounted. This avoids keeping a reference
+ // to the original IC.
+ const SharedInternalClassData<PropertyKey> nameMap = locked->d()->nameMap;
+
+ for (uint i = 0, end = locked->d()->size; i < end; ++i) {
+ const PropertyKey key = nameMap.at(i);
+ if (!key.isStringOrSymbol())
+ continue;
+ if ((member = *object->propertyData(i))) {
+ stack.push_back(member->d());
+ if (std::find(writableBegin, writableEnd, key) == writableEnd) {
+ PropertyAttributes attributes = locked->d()->find(key).attributes;
+ attributes.setConfigurable(false);
+ attributes.setWritable(false);
+ locked = locked->changeMember(key, attributes);
+ }
+ }
+ }
- auto newModule = compileModule(url);
- if (newModule) {
- moduleGuard.relock();
- modules.insert(url, newModule);
+ object->setInternalClass(locked->d());
}
+}
- return newModule;
+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.size() <= index)
+ m_extensionData.resize(index + 1);
+
+ if (m_extensionData.at(index))
+ delete m_extensionData.at(index);
+
+ m_extensionData[index] = data;
+}
+
+template<typename Source>
+bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
+{
+ QSequentialIterable iterable;
+ if (!QMetaType::view(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
+ return false;
+
+ const QMetaType elementMetaType = iterable.valueMetaType();
+ QVariant element(elementMetaType);
+ for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
+ if (!ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()))
+ element = QVariant(elementMetaType);
+ iterable.addValue(element, QSequentialIterable::AtEnd);
+ }
+ return true;
}
// 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.
-bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
+bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, void *data)
{
// check if it's one of the types we know
- switch (QMetaType::Type(type)) {
+ switch (metaType.id()) {
case QMetaType::Bool:
- *reinterpret_cast<bool*>(data) = value->toBoolean();
+ *reinterpret_cast<bool*>(data) = value.toBoolean();
return true;
case QMetaType::Int:
- *reinterpret_cast<int*>(data) = value->toInt32();
+ *reinterpret_cast<int*>(data) = value.toInt32();
return true;
case QMetaType::UInt:
- *reinterpret_cast<uint*>(data) = value->toUInt32();
+ *reinterpret_cast<uint*>(data) = value.toUInt32();
+ return true;
+ case QMetaType::Long:
+ *reinterpret_cast<long*>(data) = long(value.toInteger());
+ return true;
+ case QMetaType::ULong:
+ *reinterpret_cast<ulong*>(data) = ulong(value.toInteger());
return true;
case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(data) = qlonglong(value->toInteger());
+ *reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger());
return true;
case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(data) = qulonglong(value->toInteger());
+ *reinterpret_cast<qulonglong*>(data) = qulonglong(value.toInteger());
return true;
case QMetaType::Double:
- *reinterpret_cast<double*>(data) = value->toNumber();
+ *reinterpret_cast<double*>(data) = value.toNumber();
return true;
case QMetaType::QString:
- if (value->isUndefined() || value->isNull())
- *reinterpret_cast<QString*>(data) = QString();
+ if (value.isUndefined())
+ *reinterpret_cast<QString*>(data) = QStringLiteral("undefined");
+ else if (value.isNull())
+ *reinterpret_cast<QString*>(data) = QStringLiteral("null");
else
- *reinterpret_cast<QString*>(data) = value->toQString();
+ *reinterpret_cast<QString*>(data) = value.toQString();
return true;
case QMetaType::QByteArray:
- if (const ArrayBuffer *ab = value->as<ArrayBuffer>())
+ if (const ArrayBuffer *ab = value.as<ArrayBuffer>()) {
*reinterpret_cast<QByteArray*>(data) = ab->asByteArray();
- else
+ } else if (const String *string = value.as<String>()) {
+ *reinterpret_cast<QByteArray*>(data) = string->toQString().toUtf8();
+ } else if (const ArrayObject *ao = value.as<ArrayObject>()) {
+ // Since QByteArray is sequentially iterable, we have to construct it from a JS Array.
+ QByteArray result;
+ const qint64 length = ao->getLength();
+ result.reserve(length);
+ for (qint64 i = 0; i < length; ++i) {
+ char value = 0;
+ ExecutionEngine::metaTypeFromJS(ao->get(i), QMetaType::fromType<char>(), &value);
+ result.push_back(value);
+ }
+ *reinterpret_cast<QByteArray*>(data) = std::move(result);
+ } else {
*reinterpret_cast<QByteArray*>(data) = QByteArray();
+ }
return true;
case QMetaType::Float:
- *reinterpret_cast<float*>(data) = value->toNumber();
+ *reinterpret_cast<float*>(data) = value.toNumber();
return true;
case QMetaType::Short:
- *reinterpret_cast<short*>(data) = short(value->toInt32());
+ *reinterpret_cast<short*>(data) = short(value.toInt32());
return true;
case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(data) = value->toUInt16();
+ *reinterpret_cast<unsigned short*>(data) = value.toUInt16();
return true;
case QMetaType::Char:
- *reinterpret_cast<char*>(data) = char(value->toInt32());
+ *reinterpret_cast<char*>(data) = char(value.toInt32());
return true;
case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->toInt32());
+ *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32());
+ return true;
+ case QMetaType::SChar:
+ *reinterpret_cast<signed char*>(data) = (signed char)(value.toInt32());
return true;
case QMetaType::QChar:
- if (String *s = value->stringValue()) {
+ if (String *s = value.stringValue()) {
QString str = s->toQString();
*reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
} else {
- *reinterpret_cast<QChar*>(data) = QChar(ushort(value->toUInt16()));
+ *reinterpret_cast<QChar*>(data) = QChar(ushort(value.toUInt16()));
}
return true;
case QMetaType::QDateTime:
- if (const QV4::DateObject *d = value->as<DateObject>()) {
+ if (const QV4::DateObject *d = value.as<DateObject>()) {
*reinterpret_cast<QDateTime *>(data) = d->toQDateTime();
return true;
} break;
case QMetaType::QDate:
- if (const QV4::DateObject *d = value->as<DateObject>()) {
- *reinterpret_cast<QDate *>(data) = d->toQDateTime().date();
+ if (const QV4::DateObject *d = value.as<DateObject>()) {
+ *reinterpret_cast<QDate *>(data) = DateObject::dateTimeToDate(d->toQDateTime());
return true;
} break;
- case QMetaType::QRegExp:
- if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) {
- *reinterpret_cast<QRegExp *>(data) = r->toQRegExp();
+ case QMetaType::QTime:
+ if (const QV4::DateObject *d = value.as<DateObject>()) {
+ *reinterpret_cast<QTime *>(data) = d->toQDateTime().time();
return true;
} break;
- case QMetaType::QObjectStar: {
- const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>();
- if (qobjectWrapper || value->isNull()) {
- *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(this, *value);
+ case QMetaType::QUrl:
+ if (String *s = value.stringValue()) {
+ *reinterpret_cast<QUrl *>(data) = QUrl(s->toQString());
+ return true;
+ } else if (const QV4::UrlObject *d = value.as<UrlObject>()) {
+ *reinterpret_cast<QUrl *>(data) = d->toQUrl();
+ return true;
+ } else if (const QV4::VariantObject *d = value.as<VariantObject>()) {
+ const QVariant *variant = &d->d()->data();
+ if (variant->metaType() == QMetaType::fromType<QUrl>()) {
+ *reinterpret_cast<QUrl *>(data)
+ = *reinterpret_cast<const QUrl *>(variant->constData());
+ 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: {
+ if (value.isNull()) {
+ *reinterpret_cast<QObject* *>(data) = nullptr;
+ return true;
+ }
+ if (value.as<QV4::QObjectWrapper>()) {
+ *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
+ return true;
+ }
+ break;
}
case QMetaType::QStringList: {
- const QV4::ArrayObject *a = value->as<QV4::ArrayObject>();
+ const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
if (a) {
*reinterpret_cast<QStringList *>(data) = a->toQStringList();
return true;
@@ -1954,33 +2631,48 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
break;
}
case QMetaType::QVariantList: {
- const QV4::ArrayObject *a = value->as<QV4::ArrayObject>();
+ const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
if (a) {
- *reinterpret_cast<QVariantList *>(data) = toVariant(*a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList();
+ *reinterpret_cast<QVariantList *>(data) = ExecutionEngine::toVariant(
+ *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false)
+ .toList();
return true;
}
break;
}
case QMetaType::QVariantMap: {
- const QV4::Object *o = value->as<QV4::Object>();
+ const QV4::Object *o = value.as<QV4::Object>();
if (o) {
- *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(o);
+ *reinterpret_cast<QVariantMap *>(data) = o->engine()->variantMapFromJS(o);
return true;
}
break;
}
case QMetaType::QVariant:
- *reinterpret_cast<QVariant*>(data) = toVariant(*value, /*typeHint*/-1, /*createJSValueForObjects*/false);
+ if (value.as<QV4::Managed>()) {
+ *reinterpret_cast<QVariant*>(data) = ExecutionEngine::toVariant(
+ value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false);
+ } else if (value.isNull()) {
+ *reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr);
+ } else if (value.isUndefined()) {
+ *reinterpret_cast<QVariant*>(data) = QVariant();
+ } else if (value.isBoolean()) {
+ *reinterpret_cast<QVariant*>(data) = QVariant(value.booleanValue());
+ } else if (value.isInteger()) {
+ *reinterpret_cast<QVariant*>(data) = QVariant(value.integerValue());
+ } else if (value.isDouble()) {
+ *reinterpret_cast<QVariant*>(data) = QVariant(value.doubleValue());
+ }
return true;
case QMetaType::QJsonValue:
- *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(*value);
+ *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value);
return true;
case QMetaType::QJsonObject: {
- *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value->as<Object>());
+ *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value.as<Object>());
return true;
}
case QMetaType::QJsonArray: {
- const QV4::ArrayObject *a = value->as<ArrayObject>();
+ const QV4::ArrayObject *a = value.as<ArrayObject>();
if (a) {
*reinterpret_cast<QJsonArray *>(data) = JsonObject::toJsonArray(a);
return true;
@@ -1988,92 +2680,166 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
break;
}
default:
- ;
+ break;
}
- {
- const QQmlValueTypeWrapper *vtw = value->as<QQmlValueTypeWrapper>();
- if (vtw && vtw->typeId() == type) {
- return vtw->toGadget(data);
- }
+ if (metaType.flags() & QMetaType::IsEnumeration) {
+ *reinterpret_cast<int *>(data) = value.toInt32();
+ return true;
}
-#if 0
- if (isQtVariant(value)) {
- const QVariant &var = variantValue(value);
- // ### Enable once constructInPlace() is in qt master.
- if (var.userType() == type) {
- QMetaType::constructInPlace(type, data, var.constData());
+ if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (metaType == QMetaType::fromType<QQmlListReference>()) {
+ *reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference();
return true;
}
- if (var.canConvert(type)) {
- QVariant vv = var;
- vv.convert(type);
- Q_ASSERT(vv.userType() == type);
- QMetaType::constructInPlace(type, data, vv.constData());
+
+ const auto wrapperPrivate = wrapper->d();
+ if (wrapperPrivate->propertyType() == metaType) {
+ *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapperPrivate->property();
return true;
}
+ }
+ if (const QQmlValueTypeWrapper *vtw = value.as<QQmlValueTypeWrapper>()) {
+ const QMetaType valueType = vtw->type();
+ if (valueType == metaType)
+ return vtw->toGadget(data);
+
+ Heap::QQmlValueTypeWrapper *d = vtw->d();
+ if (d->isReference())
+ d->readReference();
+
+ if (void *gadgetPtr = d->gadgetPtr()) {
+ if (QQmlValueTypeProvider::populateValueType(metaType, data, valueType, gadgetPtr))
+ return true;
+ if (QMetaType::canConvert(valueType, metaType))
+ return QMetaType::convert(valueType, gadgetPtr, metaType, data);
+ } else {
+ QVariant empty(valueType);
+ if (QQmlValueTypeProvider::populateValueType(metaType, data, valueType, empty.data()))
+ return true;
+ if (QMetaType::canConvert(valueType, metaType))
+ return QMetaType::convert(valueType, empty.data(), metaType, data);
+ }
}
-#endif
// Try to use magic; for compatibility with qjsvalue_cast.
- QByteArray name = QMetaType::typeName(type);
- if (convertToNativeQObject(this, *value, name, reinterpret_cast<void* *>(data)))
+ if (convertToNativeQObject(value, metaType, reinterpret_cast<void **>(data)))
return true;
- if (value->as<QV4::VariantObject>() && name.endsWith('*')) {
- int valueType = QMetaType::type(name.left(name.size()-1));
- QVariant &var = value->as<QV4::VariantObject>()->d()->data();
- if (valueType == var.userType()) {
- // We have T t, T* is requested, so return &t.
- *reinterpret_cast<void* *>(data) = var.data();
+
+ const bool isPointer = (metaType.flags() & QMetaType::IsPointer);
+ const QV4::VariantObject *variantObject = value.as<QV4::VariantObject>();
+ if (variantObject) {
+ // Actually a reference, because we're poking it for its data() below and we want
+ // the _original_ data, not some copy.
+ QVariant &var = variantObject->d()->data();
+
+ if (var.metaType() == metaType) {
+ metaType.destruct(data);
+ metaType.construct(data, var.data());
return true;
- } else if (Object *o = value->objectValue()) {
- // Look in the prototype chain.
- QV4::Scope scope(this);
- QV4::ScopedObject proto(scope, o->getPrototypeOf());
- while (proto) {
- bool canCast = false;
- if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
- const QVariant &v = vo->d()->data();
- canCast = (type == v.userType()) || (valueType && (valueType == v.userType()));
- }
- else if (proto->as<QV4::QObjectWrapper>()) {
- QByteArray className = name.left(name.size()-1);
- QV4::ScopedObject p(scope, proto.getPointer());
- if (QObject *qobject = qtObjectFromJS(this, p))
- canCast = qobject->qt_metacast(className) != nullptr;
- }
- if (canCast) {
- QByteArray varTypeName = QMetaType::typeName(var.userType());
- if (varTypeName.endsWith('*'))
- *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
- else
- *reinterpret_cast<void* *>(data) = var.data();
- return true;
+ }
+
+ if (isPointer) {
+ const QByteArray pointedToTypeName = QByteArray(metaType.name()).chopped(1);
+ const QMetaType valueType = QMetaType::fromName(pointedToTypeName);
+
+ if (valueType == var.metaType()) {
+ // ### Qt7: Remove this. Returning pointers to potentially gc'd data is crazy.
+ // We have T t, T* is requested, so return &t.
+ *reinterpret_cast<const void **>(data) = var.data();
+ return true;
+ } else if (Object *o = value.objectValue()) {
+ // Look in the prototype chain.
+ QV4::Scope scope(o->engine());
+ QV4::ScopedObject proto(scope, o->getPrototypeOf());
+ while (proto) {
+ bool canCast = false;
+ if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
+ const QVariant &v = vo->d()->data();
+ canCast = (metaType == v.metaType());
+ }
+ else if (proto->as<QV4::QObjectWrapper>()) {
+ QV4::ScopedObject p(scope, proto.getPointer());
+ if (QObject *qobject = qtObjectFromJS(p)) {
+ if (const QMetaObject *metaObject = metaType.metaObject())
+ canCast = metaObject->cast(qobject) != nullptr;
+ else
+ canCast = qobject->qt_metacast(pointedToTypeName);
+ }
+ }
+ if (canCast) {
+ const QMetaType varType = var.metaType();
+ if (varType.flags() & QMetaType::IsPointer) {
+ *reinterpret_cast<const void **>(data)
+ = *reinterpret_cast<void *const *>(var.data());
+ } else {
+ *reinterpret_cast<const void **>(data) = var.data();
+ }
+ return true;
+ }
+ proto = proto->getPrototypeOf();
}
- proto = proto->getPrototypeOf();
}
+ } else if (QQmlValueTypeProvider::populateValueType(
+ metaType, data, var.metaType(), var.data())) {
+ return true;
}
- } else if (value->isNull() && name.endsWith('*')) {
+ } else if (value.isNull() && isPointer) {
*reinterpret_cast<void* *>(data) = nullptr;
return true;
- } else if (type == qMetaTypeId<QJSValue>()) {
- *reinterpret_cast<QJSValue*>(data) = QJSValue(this, value->asReturnedValue());
+ } else if (metaType == QMetaType::fromType<QJSValue>()) {
+ QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue());
+ return true;
+ } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ *reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value);
return true;
+ } else if (!isPointer) {
+ if (QQmlValueTypeProvider::populateValueType(metaType, data, value))
+ return true;
+ }
+
+ if (const QV4::Sequence *sequence = value.as<Sequence>()) {
+ const QVariant result = QV4::SequencePrototype::toVariant(sequence);
+ if (result.metaType() == metaType) {
+ metaType.destruct(data);
+ metaType.construct(data, result.constData());
+ return true;
+ }
+
+ if (convertToIterable(metaType, data, sequence))
+ return true;
+ }
+
+ if (const QV4::ArrayObject *array = value.as<ArrayObject>()) {
+ if (convertToIterable(metaType, data, array))
+ return true;
}
return false;
}
-static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result)
+static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result)
{
- if (!targetType.endsWith('*'))
+ if (!(targetType.flags() & QMetaType::IsPointer))
return false;
- if (QObject *qobject = qtObjectFromJS(e, value)) {
- int start = targetType.startsWith("const ") ? 6 : 0;
- QByteArray className = targetType.mid(start, targetType.size()-start-1);
+ if (QObject *qobject = qtObjectFromJS(value)) {
+ // If the target type has a metaObject, use that for casting.
+ if (const QMetaObject *targetMetaObject = targetType.metaObject()) {
+ if (QObject *instance = targetMetaObject->cast(qobject)) {
+ *result = instance;
+ return true;
+ }
+ return false;
+ }
+
+ // We have to call the generated qt_metacast rather than metaObject->cast() here so that
+ // it works for types without QMetaObject, such as QStandardItem.
+ const QByteArray targetTypeName = targetType.name();
+ const int start = targetTypeName.startsWith("const ") ? 6 : 0;
+ const QByteArray className = targetTypeName.mid(start, targetTypeName.size() - start - 1);
if (void *instance = qobject->qt_metacast(className)) {
*result = instance;
return true;
@@ -2082,12 +2848,12 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &va
return false;
}
-static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value)
+static QObject *qtObjectFromJS(const QV4::Value &value)
{
if (!value.isObject())
return nullptr;
- QV4::Scope scope(engine);
+ QV4::Scope scope(value.as<QV4::Managed>()->engine());
QV4::Scoped<QV4::VariantObject> v(scope, value);
if (v) {
@@ -2097,11 +2863,40 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &v
return *reinterpret_cast<QObject* const *>(variant.constData());
}
QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, value);
- if (!wrapper)
- return nullptr;
- return wrapper->object();
+ if (wrapper)
+ return wrapper->object();
+
+ QV4::Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, value);
+ if (typeWrapper)
+ return typeWrapper->object();
+
+ return nullptr;
}
-#endif // ifndef V4_BOOTSTRAP
+struct QV4EngineRegistrationData
+{
+ QV4EngineRegistrationData() : extensionCount(0) {}
+
+ QMutex mutex;
+ int extensionCount;
+};
+Q_GLOBAL_STATIC(QV4EngineRegistrationData, registrationData);
+
+QMutex *ExecutionEngine::registrationMutex()
+{
+ return &registrationData()->mutex;
+}
+
+int ExecutionEngine::registerExtension()
+{
+ return registrationData()->extensionCount++;
+}
+
+#if QT_CONFIG(qml_network)
+QNetworkAccessManager *QV4::detail::getNetworkAccessManager(ExecutionEngine *engine)
+{
+ return engine->qmlEngine()->networkAccessManager();
+}
+#endif // qml_network
QT_END_NAMESPACE