aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsapi
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsapi')
-rw-r--r--src/qml/jsapi/jsapi.pri12
-rw-r--r--src/qml/jsapi/qjsengine.cpp765
-rw-r--r--src/qml/jsapi/qjsengine.h346
-rw-r--r--src/qml/jsapi/qjsengine_p.h151
-rw-r--r--src/qml/jsapi/qjslist.cpp18
-rw-r--r--src/qml/jsapi/qjslist.h368
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp1145
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.h130
-rw-r--r--src/qml/jsapi/qjsprimitivevalue.cpp340
-rw-r--r--src/qml/jsapi/qjsprimitivevalue.h966
-rw-r--r--src/qml/jsapi/qjsvalue.cpp786
-rw-r--r--src/qml/jsapi/qjsvalue.h84
-rw-r--r--src/qml/jsapi/qjsvalue_p.h425
-rw-r--r--src/qml/jsapi/qjsvalueiterator.cpp46
-rw-r--r--src/qml/jsapi/qjsvalueiterator.h40
-rw-r--r--src/qml/jsapi/qjsvalueiterator_p.h40
16 files changed, 4532 insertions, 1130 deletions
diff --git a/src/qml/jsapi/jsapi.pri b/src/qml/jsapi/jsapi.pri
deleted file mode 100644
index f70588ec7b..0000000000
--- a/src/qml/jsapi/jsapi.pri
+++ /dev/null
@@ -1,12 +0,0 @@
-SOURCES += \
- $$PWD/qjsengine.cpp \
- $$PWD/qjsvalue.cpp \
- $$PWD/qjsvalueiterator.cpp \
-
-HEADERS += \
- $$PWD/qjsengine.h \
- $$PWD/qjsengine_p.h \
- $$PWD/qjsvalue.h \
- $$PWD/qjsvalue_p.h \
- $$PWD/qjsvalueiterator.h \
- $$PWD/qjsvalueiterator_p.h
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 065fbc1c0a..8346ef5d84 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qjsengine.h"
#include "qjsengine_p.h"
@@ -48,11 +12,14 @@
#include "private/qv4globalobject_p.h"
#include "private/qv4script_p.h"
#include "private/qv4runtime_p.h"
+#include <private/qv4dateobject_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmetaobjectwrapper_p.h>
#include <private/qv4stackframe_p.h>
#include <private/qv4module_p.h>
+#include <private/qv4symbol_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qmetaobject.h>
@@ -71,11 +38,6 @@
#include <private/qqmlglobal_p.h>
#include <qqmlengine.h>
-#undef Q_D
-#undef Q_Q
-#define Q_D(blah)
-#define Q_Q(blah)
-
Q_DECLARE_METATYPE(QList<int>)
/*!
@@ -154,6 +116,46 @@ Q_DECLARE_METATYPE(QList<int>)
}
\endcode
+ Modules don't have to be files. They can be values registered with
+ QJSEngine::registerModule():
+
+ \code
+ import version from "version";
+
+ export function getVersion()
+ {
+ return version;
+ }
+ \endcode
+
+ \code
+ QJSValue version(610);
+ myEngine.registerModule("version", version);
+ QJSValue module = myEngine.importModule("./myprint.mjs");
+ QJSValue getVersion = module.property("getVersion");
+ QJSValue result = getVersion.call();
+ \endcode
+
+ Named exports are supported, but because they are treated as members of an
+ object, the default export must be an ECMAScript object. Most of the newXYZ
+ functions in QJSValue will return an object.
+
+ \code
+ QJSValue name("Qt6");
+ QJSValue obj = myEngine.newObject();
+ obj.setProperty("name", name);
+ myEngine.registerModule("info", obj);
+ \endcode
+
+ \code
+ import { name } from "info";
+
+ export function getName()
+ {
+ return name;
+ }
+ \endcode
+
\section1 Engine Configuration
The globalObject() function returns the \b {Global Object}
@@ -242,7 +244,7 @@ Q_DECLARE_METATYPE(QList<int>)
\section1 Extensions
QJSEngine provides a compliant ECMAScript implementation. By default,
- familiar utilities like logging are not available, but they can can be
+ familiar utilities like logging are not available, but they can be
installed via the \l installExtensions() function.
\sa QJSValue, {Making Applications Scriptable},
@@ -257,7 +259,7 @@ Q_DECLARE_METATYPE(QList<int>)
\l installExtensions().
\value TranslationExtension Indicates that translation functions (\c qsTr(),
- for example) should be installed.
+ for example) should be installed. This also installs the Qt.uiLanguage property.
\value ConsoleExtension Indicates that console functions (\c console.log(),
for example) should be installed.
@@ -344,11 +346,8 @@ QJSEngine::QJSEngine()
*/
QJSEngine::QJSEngine(QObject *parent)
- : QObject(*new QJSEnginePrivate, parent)
- , m_v4Engine(new QV4::ExecutionEngine(this))
+ : QJSEngine(*new QJSEnginePrivate, parent)
{
- checkForApplicationInstance();
-
QJSEnginePrivate::addToDebugServer(this);
}
@@ -366,11 +365,12 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
Destroys this QJSEngine.
Garbage is not collected from the persistent JS heap during QJSEngine
- destruction. If you need all memory freed, call collectGarbage manually
+ destruction. If you need all memory freed, call collectGarbage() manually
right before destroying the QJSEngine.
*/
QJSEngine::~QJSEngine()
{
+ m_v4Engine->inShutdown = true;
QJSEnginePrivate::removeFromDebugServer(this);
delete m_v4Engine;
}
@@ -390,46 +390,16 @@ QJSEngine::~QJSEngine()
when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects
have been created). However, you can call this function to explicitly request that garbage
collection should be performed as soon as possible.
-*/
-void QJSEngine::collectGarbage()
-{
- m_v4Engine->memoryManager->runGC();
-}
-
-#if QT_DEPRECATED_SINCE(5, 6)
-
-/*!
- \since 5.4
- \obsolete
-
- Installs translator functions on the given \a object, or on the Global
- Object if no object is specified.
-
- The relation between script translator functions and C++ translator
- functions is described in the following table:
-
- \table
- \header \li Script Function \li Corresponding C++ Function
- \row \li qsTr() \li QObject::tr()
- \row \li QT_TR_NOOP() \li QT_TR_NOOP()
- \row \li qsTranslate() \li QCoreApplication::translate()
- \row \li QT_TRANSLATE_NOOP() \li QT_TRANSLATE_NOOP()
- \row \li qsTrId() \li qtTrId()
- \row \li QT_TRID_NOOP() \li QT_TRID_NOOP()
- \endtable
- It also adds an arg() method to the string prototype.
- \sa {Internationalization with Qt}
-*/
-void QJSEngine::installTranslatorFunctions(const QJSValue &object)
+ \sa {Garbage Collection}
+ \sa {Qt::}{gc()}
+ */
+void QJSEngine::collectGarbage()
{
- installExtensions(TranslationExtension, object);
+ m_v4Engine->memoryManager->runGC();
}
-#endif // QT_DEPRECATED_SINCE(5, 6)
-
-
/*!
\since 5.6
@@ -456,10 +426,7 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal
}
QV4::Scope scope(m_v4Engine);
- QV4::ScopedObject obj(scope);
- QV4::Value *val = QJSValuePrivate::getValue(&object);
- if (val)
- obj = val;
+ QV4::ScopedObject obj(scope, QJSValuePrivate::asReturnedValue(&object));
if (!obj)
obj = scope.engine->globalObject;
@@ -479,7 +446,7 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal
*/
void QJSEngine::setInterrupted(bool interrupted)
{
- m_v4Engine->isInterrupted = interrupted;
+ m_v4Engine->isInterrupted.storeRelaxed(interrupted);
}
/*!
@@ -490,7 +457,7 @@ void QJSEngine::setInterrupted(bool interrupted)
*/
bool QJSEngine::isInterrupted() const
{
- return m_v4Engine->isInterrupted.loadAcquire();
+ return m_v4Engine->isInterrupted.loadRelaxed();
}
static QUrl urlForFileName(const QString &fileName)
@@ -510,6 +477,9 @@ static QUrl urlForFileName(const QString &fileName)
The script code will be evaluated in the context of the global object.
+ \note If you need to evaluate inside a QML context, use \l QQmlExpression
+ instead.
+
The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the
engine; in this case the return value will be the exception
that was thrown (typically an \c{Error} object; see
@@ -527,12 +497,23 @@ static QUrl urlForFileName(const QString &fileName)
the file name is accessible through the "fileName" property if it is
provided with this function.
+ \a exceptionStackTrace is used to report whether an uncaught exception was
+ thrown. If you pass a non-null pointer to a QStringList to it, it will set
+ it to list of "stackframe messages" if the script threw an unhandled
+ exception, or an empty list otherwise. A stackframe message has the format
+ function name:line number:column:file name
+ \note In some cases, e.g. for native functions, function name and file name
+ can be empty and line number and column can be -1.
+
\note If an exception was thrown and the exception value is not an
Error instance (i.e., QJSValue::isError() returns \c false), the
- exception value will still be returned, but there is currently no
- API for detecting that an exception did occur in this case.
+ exception value will still be returned. Use \c exceptionStackTrace->isEmpty()
+ to distinguish whether the value was a normal or an exceptional return
+ value.
+
+ \sa QQmlExpression::evaluate
*/
-QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber)
+QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber, QStringList *exceptionStackTrace)
{
QV4::ExecutionEngine *v4 = m_v4Engine;
QV4::Scope scope(v4);
@@ -546,16 +527,27 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
script.strictMode = v4->globalCode->isStrict();
script.inheritContext = true;
script.parse();
- if (!scope.engine->hasException)
+ if (!scope.hasException())
result = script.run();
- if (scope.engine->hasException)
- result = v4->catchException();
- if (v4->isInterrupted.loadAcquire())
+ if (exceptionStackTrace)
+ exceptionStackTrace->clear();
+ if (scope.hasException()) {
+ QV4::StackTrace trace;
+ result = v4->catchException(&trace);
+ if (exceptionStackTrace) {
+ for (auto &&frame: trace)
+ exceptionStackTrace->push_back(QLatin1StringView("%1:%2:%3:%4").arg(
+ frame.function,
+ QString::number(qAbs(frame.line)),
+ QString::number(frame.column),
+ frame.source)
+ );
+ }
+ }
+ if (v4->isInterrupted.loadRelaxed())
result = v4->newErrorObject(QStringLiteral("Interrupted"));
- QJSValue retval(v4, result->asReturnedValue());
-
- return retval;
+ return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
}
/*!
@@ -576,26 +568,68 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
\note If an exception is thrown during the loading of the module, the return value
will be the exception (typically an \c{Error} object; see QJSValue::isError()).
+ \sa registerModule()
+
\since 5.12
*/
QJSValue QJSEngine::importModule(const QString &fileName)
{
const QUrl url = urlForFileName(QFileInfo(fileName).canonicalFilePath());
- auto moduleUnit = m_v4Engine->loadModule(url);
+ const auto module = m_v4Engine->loadModule(url);
if (m_v4Engine->hasException)
- return QJSValue(m_v4Engine, m_v4Engine->catchException());
+ return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
+
+ QV4::Scope scope(m_v4Engine);
+ if (const auto compiled = module.compiled) {
+ QV4::Scoped<QV4::Module> moduleNamespace(scope, compiled->instantiate());
+ if (m_v4Engine->hasException)
+ return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
+ compiled->evaluate();
+ if (!m_v4Engine->isInterrupted.loadRelaxed())
+ return QJSValuePrivate::fromReturnedValue(moduleNamespace->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(
+ m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
+ }
+
+ // If there is neither a native nor a compiled module, we should have seen an exception
+ Q_ASSERT(module.native);
+
+ return QJSValuePrivate::fromReturnedValue(module.native->asReturnedValue());
+}
+
+/*!
+ Registers a QJSValue to serve as a module. After this function is called,
+ all modules that import \a moduleName will import the value of \a value
+ instead of loading \a moduleName from the filesystem.
+ Any valid QJSValue can be registered, but named exports (i.e.
+ \c {import { name } from "info"} are treated as members of an object, so
+ the default export must be created with one of the newXYZ methods of
+ QJSEngine.
+
+ Because this allows modules that do not exist on the filesystem to be imported,
+ scripting applications can use this to provide built-in modules, similar to
+ Node.js.
+
+ Returns \c true on success, \c false otherwise.
+
+ \note The QJSValue \a value is not called or read until it is used by another module.
+ This means that there is no code to evaluate, so no errors will be seen until
+ another module throws an exception while trying to load this module.
+
+ \warning Attempting to access a named export from a QJSValue that is not an
+ object will trigger a \l{Script Exceptions}{exception}.
+
+ \sa importModule()
+ */
+bool QJSEngine::registerModule(const QString &moduleName, const QJSValue &value)
+{
QV4::Scope scope(m_v4Engine);
- QV4::Scoped<QV4::Module> moduleNamespace(scope, moduleUnit->instantiate(m_v4Engine));
+ QV4::ScopedValue v4Value(scope, QJSValuePrivate::asReturnedValue(&value));
+ m_v4Engine->registerNativeModule(QUrl(moduleName), v4Value);
if (m_v4Engine->hasException)
- return QJSValue(m_v4Engine, m_v4Engine->catchException());
- moduleUnit->evaluate();
- if (!m_v4Engine->isInterrupted.loadAcquire())
- return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue());
-
- return QJSValue(
- m_v4Engine,
- m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
+ return false;
+ return true;
}
/*!
@@ -610,7 +644,23 @@ QJSValue QJSEngine::newObject()
{
QV4::Scope scope(m_v4Engine);
QV4::ScopedValue v(scope, m_v4Engine->newObject());
- return QJSValue(m_v4Engine, v->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
+}
+
+/*!
+ \since 6.2
+
+ Creates a JavaScript object of class Symbol, with value \a name.
+
+ The prototype of the created object will be the Symbol prototype object.
+
+ \sa newObject()
+*/
+QJSValue QJSEngine::newSymbol(const QString &name)
+{
+ QV4::Scope scope(m_v4Engine);
+ QV4::ScopedValue v(scope, QV4::Symbol::create(m_v4Engine, u'@' + name));
+ return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
}
/*!
@@ -652,7 +702,7 @@ QJSValue QJSEngine::newErrorObject(QJSValue::ErrorType errorType, const QString
case QJSValue::NoError:
return QJSValue::UndefinedValue;
}
- return QJSValue(m_v4Engine, error->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(error->asReturnedValue());
}
/*!
@@ -667,7 +717,7 @@ QJSValue QJSEngine::newArray(uint length)
if (length < 0x1000)
array->arrayReserve(length);
array->setArrayLengthUnchecked(length);
- return QJSValue(m_v4Engine, array.asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(array.asReturnedValue());
}
/*!
@@ -692,7 +742,6 @@ QJSValue QJSEngine::newArray(uint length)
*/
QJSValue QJSEngine::newQObject(QObject *object)
{
- Q_D(QJSEngine);
QV4::ExecutionEngine *v4 = m_v4Engine;
QV4::Scope scope(v4);
if (object) {
@@ -701,7 +750,7 @@ QJSValue QJSEngine::newQObject(QObject *object)
QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
}
QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(v4, object));
- return QJSValue(v4, v->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
}
/*!
@@ -719,11 +768,10 @@ QJSValue QJSEngine::newQObject(QObject *object)
*/
QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) {
- Q_D(QJSEngine);
QV4::ExecutionEngine *v4 = m_v4Engine;
QV4::Scope scope(v4);
QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject));
- return QJSValue(v4, v->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
}
/*! \fn template <typename T> QJSValue QJSEngine::newQMetaObject()
@@ -750,152 +798,252 @@ QJSValue QJSEngine::globalObject() const
{
QV4::Scope scope(m_v4Engine);
QV4::ScopedValue v(scope, m_v4Engine->globalObject);
- return QJSValue(m_v4Engine, v->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
+}
+
+QJSPrimitiveValue QJSEngine::createPrimitive(QMetaType type, const void *ptr)
+{
+ QV4::Scope scope(m_v4Engine);
+ QV4::ScopedValue v(scope, m_v4Engine->metaTypeToJS(type, ptr));
+ return QV4::ExecutionEngine::createPrimitive(v);
+}
+
+QJSManagedValue QJSEngine::createManaged(QMetaType type, const void *ptr)
+{
+ QJSManagedValue result(m_v4Engine);
+ *result.d = m_v4Engine->metaTypeToJS(type, ptr);
+ return result;
}
/*!
* \internal
* used by QJSEngine::toScriptValue
*/
-QJSValue QJSEngine::create(int type, const void *ptr)
+QJSValue QJSEngine::create(QMetaType type, const void *ptr)
{
QV4::Scope scope(m_v4Engine);
QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, ptr));
- return QJSValue(m_v4Engine, v->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
+}
+
+bool QJSEngine::convertPrimitive(const QJSPrimitiveValue &value, QMetaType type, void *ptr)
+{
+ switch (value.type()) {
+ case QJSPrimitiveValue::Undefined:
+ return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::undefinedValue(), type, ptr);
+ case QJSPrimitiveValue::Null:
+ return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::nullValue(), type, ptr);
+ case QJSPrimitiveValue::Boolean:
+ return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::fromBoolean(value.toBoolean()), type, ptr);
+ case QJSPrimitiveValue::Integer:
+ return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::fromInt32(value.toInteger()), type, ptr);
+ case QJSPrimitiveValue::Double:
+ return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::fromDouble(value.toDouble()), type, ptr);
+ case QJSPrimitiveValue::String:
+ return convertString(value.toString(), type, ptr);
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
+bool QJSEngine::convertManaged(const QJSManagedValue &value, int type, void *ptr)
+{
+ return convertManaged(value, QMetaType(type), ptr);
+}
+
+bool QJSEngine::convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr)
+{
+ return QV4::ExecutionEngine::metaTypeFromJS(*value.d, type, ptr);
+}
+
+bool QJSEngine::convertString(const QString &string, QMetaType metaType, void *ptr)
+{
+ // have a string based value without engine. Do conversion manually
+ if (metaType == QMetaType::fromType<bool>()) {
+ *reinterpret_cast<bool*>(ptr) = string.size() != 0;
+ return true;
+ }
+ if (metaType == QMetaType::fromType<QString>()) {
+ *reinterpret_cast<QString*>(ptr) = string;
+ return true;
+ }
+ if (metaType == QMetaType::fromType<QUrl>()) {
+ *reinterpret_cast<QUrl *>(ptr) = QUrl(string);
+ return true;
+ }
+
+ double d = QV4::RuntimeHelpers::stringToNumber(string);
+ switch (metaType.id()) {
+ case QMetaType::Int:
+ *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UInt:
+ *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::Long:
+ *reinterpret_cast<long*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::ULong:
+ *reinterpret_cast<ulong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::LongLong:
+ *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::ULongLong:
+ *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
+ return true;
+ case QMetaType::Double:
+ *reinterpret_cast<double*>(ptr) = d;
+ return true;
+ case QMetaType::Float:
+ *reinterpret_cast<float*>(ptr) = d;
+ return true;
+ case QMetaType::Short:
+ *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UShort:
+ *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::Char:
+ *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
+ return true;
+ case QMetaType::UChar:
+ *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ case QMetaType::QChar:
+ *reinterpret_cast<QChar*>(ptr) = QChar(QV4::Value::toUInt32(d));
+ return true;
+ case QMetaType::Char16:
+ *reinterpret_cast<char16_t *>(ptr) = QV4::Value::toUInt32(d);
+ return true;
+ default:
+ return false;
+ }
}
/*!
\internal
convert \a value to \a type, store the result in \a ptr
*/
-bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
+bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr)
{
- QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(&value, &scratch);
- if (v4) {
- QV4::Scope scope(v4);
- QV4::ScopedValue v(scope, *val);
- return scope.engine->metaTypeFromJS(v, type, ptr);
- }
+ if (const QString *string = QJSValuePrivate::asQString(&value))
+ return convertString(*string, metaType, ptr);
- if (!val) {
- QVariant *variant = QJSValuePrivate::getVariant(&value);
- Q_ASSERT(variant);
-
- if (variant->userType() == QMetaType::QString) {
- QString string = variant->toString();
- // have a string based value without engine. Do conversion manually
- if (type == QMetaType::Bool) {
- *reinterpret_cast<bool*>(ptr) = string.length() != 0;
- return true;
- }
- if (type == QMetaType::QString) {
- *reinterpret_cast<QString*>(ptr) = string;
- return true;
- }
- double d = QV4::RuntimeHelpers::stringToNumber(string);
- switch (type) {
- case QMetaType::Int:
- *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UInt:
- *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
- return true;
- case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
- return true;
- case QMetaType::Double:
- *reinterpret_cast<double*>(ptr) = d;
- return true;
- case QMetaType::Float:
- *reinterpret_cast<float*>(ptr) = d;
- return true;
- case QMetaType::Short:
- *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::Char:
- *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
- return true;
- case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- case QMetaType::QChar:
- *reinterpret_cast<QChar*>(ptr) = QV4::Value::toUInt32(d);
- return true;
- default:
- return false;
- }
- } else {
- return QMetaType::convert(&variant->data_ptr(), variant->userType(), ptr, type);
- }
- }
+ // Does not need scoping since QJSValue still holds on to the value.
+ return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, ptr);
+}
- Q_ASSERT(val);
-
- switch (type) {
- case QMetaType::Bool:
- *reinterpret_cast<bool*>(ptr) = val->toBoolean();
- return true;
- case QMetaType::Int:
- *reinterpret_cast<int*>(ptr) = val->toInt32();
- return true;
- case QMetaType::UInt:
- *reinterpret_cast<uint*>(ptr) = val->toUInt32();
- return true;
- case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(ptr) = val->toInteger();
- return true;
- case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(ptr) = val->toInteger();
- return true;
- case QMetaType::Double:
- *reinterpret_cast<double*>(ptr) = val->toNumber();
- return true;
- case QMetaType::QString:
- *reinterpret_cast<QString*>(ptr) = val->toQStringNoThrow();
- return true;
- case QMetaType::Float:
- *reinterpret_cast<float*>(ptr) = val->toNumber();
- return true;
- case QMetaType::Short:
- *reinterpret_cast<short*>(ptr) = val->toInt32();
- return true;
- case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(ptr) = val->toUInt16();
- return true;
- case QMetaType::Char:
- *reinterpret_cast<char*>(ptr) = val->toInt32();
- return true;
- case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(ptr) = val->toUInt16();
- return true;
- case QMetaType::QChar:
- *reinterpret_cast<QChar*>(ptr) = val->toUInt16();
- return true;
- default:
- return false;
- }
+bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr)
+{
+ // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
+ // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
+ QV4::Scope scope(handle());
+ QV4::ScopedValue scoped(scope, scope.engine->fromVariant(value));
+ return QV4::ExecutionEngine::metaTypeFromJS(scoped, metaType, ptr);
+}
+
+bool QJSEngine::convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to)
+{
+ // TODO: We could probably avoid creating a QV4::Value in many cases, but we'd have to
+ // duplicate much of metaTypeFromJS and some methods of QV4::Value itself here.
+ QV4::Scope scope(handle());
+ QV4::ScopedValue scoped(scope, scope.engine->fromData(fromType, from));
+ return QV4::ExecutionEngine::metaTypeFromJS(scoped, toType, to);
+}
+
+QString QJSEngine::convertQObjectToString(QObject *object)
+{
+ return QV4::QObjectWrapper::objectToString(
+ handle(), object ? object->metaObject() : nullptr, object);
+}
+
+QString QJSEngine::convertDateTimeToString(const QDateTime &dateTime)
+{
+ return QV4::DateObject::dateTimeToString(dateTime, handle());
+}
+
+double QJSEngine::convertDateTimeToNumber(const QDateTime &dateTime)
+{
+ return QV4::DateObject::dateTimeToNumber(dateTime);
+}
+
+QDate QJSEngine::convertDateTimeToDate(const QDateTime &dateTime)
+{
+ return QV4::DateObject::dateTimeToDate(dateTime);
}
/*! \fn template <typename T> QJSValue QJSEngine::toScriptValue(const T &value)
Creates a QJSValue with the given \a value.
- \sa fromScriptValue()
+ \sa fromScriptValue(), coerceValue()
+*/
+
+/*! \fn template <typename T> QJSManagedValue QJSEngine::toManagedValue(const T &value)
+
+ Creates a QJSManagedValue with the given \a value.
+
+ \sa fromManagedValue(), coerceValue()
+*/
+
+/*! \fn template <typename T> QJSPrimitiveValue QJSEngine::toPrimitiveValue(const T &value)
+
+ Creates a QJSPrimitiveValue with the given \a value.
+
+ Since QJSPrimitiveValue can only hold int, bool, double, QString, and the
+ equivalents of JavaScript \c null and \c undefined, the value will be
+ coerced aggressively if you pass any other type.
+
+ \sa fromPrimitiveValue(), coerceValue()
*/
/*! \fn template <typename T> T QJSEngine::fromScriptValue(const QJSValue &value)
Returns the given \a value converted to the template type \c{T}.
- \sa toScriptValue()
+ \sa toScriptValue(), coerceValue()
+*/
+
+/*! \fn template <typename T> T QJSEngine::fromManagedValue(const QJSManagedValue &value)
+
+ Returns the given \a value converted to the template type \c{T}.
+
+ \sa toManagedValue(), coerceValue()
+*/
+
+/*! \fn template <typename T> T QJSEngine::fromPrimitiveValue(const QJSPrimitiveValue &value)
+
+ Returns the given \a value converted to the template type \c{T}.
+
+ Since QJSPrimitiveValue can only hold int, bool, double, QString, and the
+ equivalents of JavaScript \c null and \c undefined, the value will be
+ coerced aggressively if you request any other type.
+
+ \sa toPrimitiveValue(), coerceValue()
+*/
+
+/*! \fn template <typename T> T QJSEngine::fromVariant(const QVariant &value)
+
+ Returns the given \a value converted to the template type \c{T}.
+ The conversion is done in JavaScript semantics. Those differ from
+ qvariant_cast's semantics. There are a number of implicit
+ conversions between JavaScript-equivalent types that are not
+ performed by qvariant_cast by default.
+
+ \sa coerceValue(), fromScriptValue(), {QVariant::}{qvariant_cast()}
+*/
+
+/*! \fn template <typename From, typename To> T QJSEngine::coerceValue(const From &from)
+
+ Returns the given \a from converted to the template type \c{To}.
+ The conversion is done in JavaScript semantics. Those differ from
+ qvariant_cast's semantics. There are a number of implicit
+ conversions between JavaScript-equivalent types that are not
+ performed by qvariant_cast by default. This method is a generalization of
+ all the other conversion methods in this class.
+
+ \sa fromVariant(), {QVariant::}{qvariant_cast()}, fromScriptValue(), toScriptValue()
*/
/*!
@@ -907,7 +1055,7 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
JavaScript function through QJSEngine.
When returning from C++, the engine will interrupt the normal flow of
- execution and call the the next pre-registered exception handler with
+ execution and call the next pre-registered exception handler with
an error object that contains the given \a message. The error object
will point to the location of the top-most context on the JavaScript
caller stack; specifically, it will have properties \c lineNumber,
@@ -998,12 +1146,78 @@ void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message
{
QV4::Scope scope(m_v4Engine);
QJSValue error = newErrorObject(errorType, message);
- QV4::ScopedObject e(scope, QJSValuePrivate::getValue(&error));
+ QV4::ScopedObject e(scope, QJSValuePrivate::asReturnedValue(&error));
if (!e)
return;
m_v4Engine->throwError(e);
}
+/*!
+ \overload throwError()
+
+ Throws a pre-constructed run-time \a error (exception). This way you can
+ use \l newErrorObject() to create the error and customize it as necessary.
+
+ \since 6.1
+ \sa {Script Exceptions}, newErrorObject()
+*/
+void QJSEngine::throwError(const QJSValue &error)
+{
+ m_v4Engine->throwError(QJSValuePrivate::asReturnedValue(&error));
+}
+
+/*!
+ * Returns \c true if the last JavaScript execution resulted in an exception or
+ * if throwError() was called. Otherwise returns \c false. Mind that evaluate()
+ * catches any exceptions thrown in the evaluated code.
+ *
+ * \since Qt 6.1
+ */
+bool QJSEngine::hasError() const
+{
+ return m_v4Engine->hasException;
+}
+
+/*!
+ * If an exception is currently pending, catches it and returns it as a
+ * QJSValue. Otherwise returns undefined as QJSValue. After calling this method
+ * hasError() returns \c false.
+ *
+ * \since Qt 6.1
+ */
+QJSValue QJSEngine::catchError()
+{
+ if (m_v4Engine->hasException)
+ return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
+ else
+ return QJSValue();
+}
+
+/*!
+ \property QJSEngine::uiLanguage
+ \brief the language to be used for translating user interface strings
+ \since 5.15
+
+ This property holds the name of the language to be used for user interface
+ string translations. It is exposed for reading and writing as \c{Qt.uiLanguage} when
+ the QJSEngine::TranslationExtension is installed on the engine. It is always exposed
+ in instances of QQmlEngine.
+
+ You can set the value freely and use it in bindings. It is recommended to set it
+ after installing translators in your application. By convention, an empty string
+ means no translation from the language used in the source code is intended to occur.
+*/
+void QJSEngine::setUiLanguage(const QString &language) {
+ Q_D(QJSEngine);
+ d->uiLanguage = language; // property takes care of signal emission if necessary
+}
+
+QString QJSEngine::uiLanguage() const
+{
+ Q_D(const QJSEngine);
+ return d->uiLanguage;
+}
+
QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e)
{
return e->jsEngine()->d_func();
@@ -1046,12 +1260,87 @@ void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q)
*/
QJSEngine *qjsEngine(const QObject *object)
{
- QQmlData *data = QQmlData::get(object, false);
+ QQmlData *data = QQmlData::get(object);
if (!data || data->jsWrapper.isNullOrUndefined())
return nullptr;
return data->jsWrapper.engine()->jsEngine();
}
+
+/*!
+ \enum QJSEngine::ObjectOwnership
+
+ ObjectOwnership controls whether or not the JavaScript memory manager automatically destroys the
+ QObject when the corresponding JavaScript object is garbage collected by the
+ engine. The two ownership options are:
+
+ \value CppOwnership The object is owned by C++ code and the JavaScript memory manager will never
+ delete it. The JavaScript destroy() method cannot be used on these objects. This
+ option is similar to QScriptEngine::QtOwnership.
+
+ \value JavaScriptOwnership The object is owned by JavaScript. When the object
+ is returned to the JavaScript memory manager as the return value of a method call, the JavaScript
+ memory manager will track it and delete it if there are no remaining JavaScript references to it
+ and it has no QObject::parent(). An object tracked by one QJSEngine will be deleted during that
+ QJSEngine's destructor. Thus, JavaScript references between objects with JavaScriptOwnership from
+ two different engines will not be valid if one of these engines is deleted. This option is similar
+ to QScriptEngine::ScriptOwnership.
+
+ Generally an application doesn't need to set an object's ownership explicitly. The JavaScript
+ memory manager uses a heuristic to set the default ownership. By default, an object that is
+ created by the JavaScript memory manager has JavaScriptOwnership. The exception to this are the
+ root objects created by calling QQmlComponent::create() or QQmlComponent::beginCreate(), which
+ have CppOwnership by default. The ownership of these root-level objects is considered to have been
+ transferred to the C++ caller.
+
+ Objects not-created by the JavaScript memory manager have CppOwnership by default. The exception
+ to this are objects returned from C++ method calls; their ownership will be set to
+ JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE methods or slots,
+ but not to property getter invocations.
+
+ Calling setObjectOwnership() overrides the default ownership.
+
+ \sa {Data Ownership}
+*/
+
+/*!
+ Sets the \a ownership of \a object.
+
+ An object with \c JavaScriptOwnership is not garbage collected as long
+ as it still has a parent, even if there are no references to it.
+
+ \sa QJSEngine::ObjectOwnership
+*/
+void QJSEngine::setObjectOwnership(QObject *object, ObjectOwnership ownership)
+{
+ if (!object)
+ return;
+
+ QQmlData *ddata = QQmlData::get(object, true);
+ if (!ddata)
+ return;
+
+ ddata->indestructible = (ownership == CppOwnership)?true:false;
+ ddata->explicitIndestructibleSet = true;
+}
+
+/*!
+ Returns the ownership of \a object.
+
+ \sa QJSEngine::ObjectOwnership
+*/
+QJSEngine::ObjectOwnership QJSEngine::objectOwnership(QObject *object)
+{
+ if (!object)
+ return CppOwnership;
+
+ QQmlData *ddata = QQmlData::get(object, false);
+ if (!ddata)
+ return CppOwnership;
+ else
+ return ddata->indestructible?CppOwnership:JavaScriptOwnership;
+}
+
QT_END_NAMESPACE
#include "moc_qjsengine.cpp"
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 31a4d68baa..b1c99db220 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QJSENGINE_H
#define QJSENGINE_H
@@ -45,8 +9,9 @@
#include <QtCore/qvariant.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qobject.h>
+#include <QtCore/qtimezone.h>
#include <QtQml/qjsvalue.h>
-
+#include <QtQml/qjsmanagedvalue.h>
#include <QtQml/qqmldebug.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +25,7 @@ class Q_QML_EXPORT QJSEngine
: public QObject
{
Q_OBJECT
+ Q_PROPERTY(QString uiLanguage READ uiLanguage WRITE setUiLanguage NOTIFY uiLanguageChanged)
public:
QJSEngine();
explicit QJSEngine(QObject *parent);
@@ -67,11 +33,13 @@ public:
QJSValue globalObject() const;
- QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1);
+ QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1, QStringList *exceptionStackTrace = nullptr);
QJSValue importModule(const QString &fileName);
+ bool registerModule(const QString &moduleName, const QJSValue &value);
QJSValue newObject();
+ QJSValue newSymbol(const QString &name);
QJSValue newArray(uint length = 0);
QJSValue newQObject(QObject *object);
@@ -89,19 +57,230 @@ public:
template <typename T>
inline QJSValue toScriptValue(const T &value)
{
- return create(qMetaTypeId<T>(), &value);
+ return create(QMetaType::fromType<T>(), &value);
+ }
+
+ template <typename T>
+ inline QJSManagedValue toManagedValue(const T &value)
+ {
+ return createManaged(QMetaType::fromType<T>(), &value);
+ }
+
+ template <typename T>
+ inline QJSPrimitiveValue toPrimitiveValue(const T &value)
+ {
+ // In the common case that the argument fits into QJSPrimitiveValue, use it.
+ if constexpr (std::disjunction_v<
+ std::is_same<T, int>,
+ std::is_same<T, bool>,
+ std::is_same<T, double>,
+ std::is_same<T, QString>>) {
+ return QJSPrimitiveValue(value);
+ } else {
+ return createPrimitive(QMetaType::fromType<T>(), &value);
+ }
}
+
template <typename T>
inline T fromScriptValue(const QJSValue &value)
{
return qjsvalue_cast<T>(value);
}
+ template <typename T>
+ inline T fromManagedValue(const QJSManagedValue &value)
+ {
+ return qjsvalue_cast<T>(value);
+ }
+
+ template <typename T>
+ inline T fromPrimitiveValue(const QJSPrimitiveValue &value)
+ {
+ if constexpr (std::is_same_v<T, int>)
+ return value.toInteger();
+ if constexpr (std::is_same_v<T, bool>)
+ return value.toBoolean();
+ if constexpr (std::is_same_v<T, double>)
+ return value.toDouble();
+ if constexpr (std::is_same_v<T, QString>)
+ return value.toString();
+ if constexpr (std::is_same_v<T, QVariant>)
+ return value.toVariant();
+ if constexpr (std::is_pointer_v<T>)
+ return nullptr;
+ return qjsvalue_cast<T>(value);
+ }
+
+ template <typename T>
+ inline T fromVariant(const QVariant &value)
+ {
+ if constexpr (std::is_same_v<T, QVariant>)
+ return value;
+
+ const QMetaType sourceType = value.metaType();
+ const QMetaType targetType = QMetaType::fromType<T>();
+ if (sourceType == targetType)
+ return *reinterpret_cast<const T *>(value.constData());
+
+ if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) {
+ using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *;
+ const QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>();
+ if (value.metaType() == nonConstTargetType)
+ return *reinterpret_cast<const nonConstT *>(value.constData());
+ }
+
+ if constexpr (std::is_same_v<T, QJSValue>)
+ return toScriptValue(value);
+
+ if constexpr (std::is_same_v<T, QJSManagedValue>)
+ return toManagedValue(value);
+
+ if constexpr (std::is_same_v<T, QJSPrimitiveValue>)
+ return toPrimitiveValue(value);
+
+ if constexpr (std::is_same_v<T, QString>) {
+ if (sourceType.flags() & QMetaType::PointerToQObject) {
+ return convertQObjectToString(
+ *reinterpret_cast<QObject *const *>(value.constData()));
+ }
+ }
+
+ if constexpr (std::is_same_v<QObject, std::remove_const_t<std::remove_pointer_t<T>>>) {
+ if (sourceType.flags() & QMetaType::PointerToQObject) {
+ return *static_cast<QObject *const *>(value.constData());
+
+ // We should not access source->metaObject() here since that may trigger some
+ // rather involved code. convertVariant() can do this using property caches.
+ }
+ }
+
+ if (sourceType == QMetaType::fromType<QJSValue>())
+ return fromScriptValue<T>(*reinterpret_cast<const QJSValue *>(value.constData()));
+
+ if (sourceType == QMetaType::fromType<QJSManagedValue>()) {
+ return fromManagedValue<T>(
+ *reinterpret_cast<const QJSManagedValue *>(value.constData()));
+ }
+
+ if (sourceType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ return fromPrimitiveValue<T>(
+ *reinterpret_cast<const QJSPrimitiveValue *>(value.constData()));
+ }
+
+ {
+ T t{};
+ if (value.metaType() == QMetaType::fromType<QString>()) {
+ if (convertString(value.toString(), targetType, &t))
+ return t;
+ } else if (convertVariant(value, targetType, &t)) {
+ return t;
+ }
+
+ QMetaType::convert(value.metaType(), value.constData(), targetType, &t);
+ return t;
+ }
+ }
+
+ template<typename From, typename To>
+ inline To coerceValue(const From &from)
+ {
+ if constexpr (std::is_base_of_v<To, From>)
+ return from;
+
+ if constexpr (std::is_same_v<To, QJSValue>)
+ return toScriptValue(from);
+
+ if constexpr (std::is_same_v<From, QJSValue>)
+ return fromScriptValue<To>(from);
+
+ if constexpr (std::is_same_v<To, QJSManagedValue>)
+ return toManagedValue(from);
+
+ if constexpr (std::is_same_v<From, QJSManagedValue>)
+ return fromManagedValue<To>(from);
+
+ if constexpr (std::is_same_v<To, QJSPrimitiveValue>)
+ return toPrimitiveValue(from);
+
+ if constexpr (std::is_same_v<From, QJSPrimitiveValue>)
+ return fromPrimitiveValue<To>(from);
+
+ if constexpr (std::is_same_v<From, QVariant>)
+ return fromVariant<To>(from);
+
+ if constexpr (std::is_same_v<To, QVariant>)
+ return QVariant::fromValue(from);
+
+ if constexpr (std::is_same_v<To, QString>) {
+ if constexpr (std::is_base_of_v<QObject, std::remove_const_t<std::remove_pointer_t<From>>>)
+ return convertQObjectToString(from);
+ }
+
+ if constexpr (std::is_same_v<From, QDateTime>) {
+ if constexpr (std::is_same_v<To, QDate>)
+ return convertDateTimeToDate(from.toLocalTime());
+ if constexpr (std::is_same_v<To, QTime>)
+ return from.toLocalTime().time();
+ if constexpr (std::is_same_v<To, QString>)
+ return convertDateTimeToString(from.toLocalTime());
+ if constexpr (std::is_same_v<To, double>)
+ return convertDateTimeToNumber(from.toLocalTime());
+ }
+
+ if constexpr (std::is_same_v<From, QDate>) {
+ if constexpr (std::is_same_v<To, QDateTime>)
+ return from.startOfDay(QTimeZone::UTC);
+ if constexpr (std::is_same_v<To, QTime>) {
+ // This is the current time zone offset, for better or worse
+ return coerceValue<QDateTime, QTime>(coerceValue<QDate, QDateTime>(from));
+ }
+ if constexpr (std::is_same_v<To, QString>)
+ return convertDateTimeToString(coerceValue<QDate, QDateTime>(from));
+ if constexpr (std::is_same_v<To, double>)
+ return convertDateTimeToNumber(coerceValue<QDate, QDateTime>(from));
+ }
+
+ if constexpr (std::is_same_v<From, QTime>) {
+ if constexpr (std::is_same_v<To, QDate>) {
+ // Yes. April Fools' 1971. See qv4dateobject.cpp.
+ return from.isValid() ? QDate(1971, 4, 1) : QDate();
+ }
+
+ if constexpr (std::is_same_v<To, QDateTime>)
+ return QDateTime(coerceValue<QTime, QDate>(from), from, QTimeZone::LocalTime);
+ if constexpr (std::is_same_v<To, QString>)
+ return convertDateTimeToString(coerceValue<QTime, QDateTime>(from));
+ if constexpr (std::is_same_v<To, double>)
+ return convertDateTimeToNumber(coerceValue<QTime, QDateTime>(from));
+ }
+
+ if constexpr (std::is_same_v<To, std::remove_const_t<std::remove_pointer_t<To>> const *>) {
+ using nonConstTo = std::remove_const_t<std::remove_pointer_t<To>> *;
+ if constexpr (std::is_same_v<From, nonConstTo>)
+ return from;
+ }
+
+ {
+ const QMetaType sourceType = QMetaType::fromType<From>();
+ const QMetaType targetType = QMetaType::fromType<To>();
+ To to{};
+ if constexpr (std::is_same_v<From, QString>) {
+ if (convertString(from, targetType, &to))
+ return to;
+ } else if (convertMetaType(sourceType, &from, targetType, &to)) {
+ return to;
+ }
+
+ QMetaType::convert(sourceType, &from, targetType, &to);
+ return to;
+ }
+ }
+
void collectGarbage();
-#if QT_DEPRECATED_SINCE(5, 6)
- QT_DEPRECATED void installTranslatorFunctions(const QJSValue &object = QJSValue());
-#endif
+ enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
+ static void setObjectOwnership(QObject *, ObjectOwnership);
+ static ObjectOwnership objectOwnership(QObject *);
enum Extension {
TranslationExtension = 0x1,
@@ -120,13 +299,49 @@ public:
void throwError(const QString &message);
void throwError(QJSValue::ErrorType errorType, const QString &message = QString());
+ void throwError(const QJSValue &error);
+ bool hasError() const;
+ QJSValue catchError();
+
+ QString uiLanguage() const;
+ void setUiLanguage(const QString &language);
+
+Q_SIGNALS:
+ void uiLanguageChanged();
private:
- QJSValue create(int type, const void *ptr);
+ QJSPrimitiveValue createPrimitive(QMetaType type, const void *ptr);
+ QJSManagedValue createManaged(QMetaType type, const void *ptr);
+ QJSValue create(QMetaType type, const void *ptr);
+#if QT_QML_REMOVED_SINCE(6, 5)
+ QJSValue create(int id, const void *ptr); // only there for BC reasons
+#endif
+
+ static bool convertPrimitive(const QJSPrimitiveValue &value, QMetaType type, void *ptr);
+ static bool convertManaged(const QJSManagedValue &value, int type, void *ptr);
+ static bool convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr);
+#if QT_QML_REMOVED_SINCE(6, 5)
+ static bool convertV2(const QJSValue &value, int type, void *ptr); // only there for BC reasons
+#endif
+ static bool convertV2(const QJSValue &value, QMetaType metaType, void *ptr);
+ static bool convertString(const QString &string, QMetaType metaType, void *ptr);
+
+ bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr);
+ bool convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to);
+
+ QString convertQObjectToString(QObject *object);
+ QString convertDateTimeToString(const QDateTime &dateTime);
+ double convertDateTimeToNumber(const QDateTime &dateTime);
+ static QDate convertDateTimeToDate(const QDateTime &dateTime);
- static bool convertV2(const QJSValue &value, int type, void *ptr);
+ template<typename T>
+ friend inline T qjsvalue_cast(const QJSValue &);
- friend inline bool qjsvalue_cast_helper(const QJSValue &, int, void *);
+ template<typename T>
+ friend inline T qjsvalue_cast(const QJSManagedValue &);
+
+ template<typename T>
+ friend inline T qjsvalue_cast(const QJSPrimitiveValue &);
protected:
QJSEngine(QJSEnginePrivate &dd, QObject *parent = nullptr);
@@ -139,23 +354,30 @@ private:
Q_DECLARE_OPERATORS_FOR_FLAGS(QJSEngine::Extensions)
-inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr)
+template<typename T>
+T qjsvalue_cast(const QJSValue &value)
{
- return QJSEngine::convertV2(value, type, ptr);
+ if (T t; QJSEngine::convertV2(value, QMetaType::fromType<T>(), &t))
+ return t;
+ return qvariant_cast<T>(value.toVariant());
}
template<typename T>
-T qjsvalue_cast(const QJSValue &value)
+T qjsvalue_cast(const QJSManagedValue &value)
{
- T t;
- const int id = qMetaTypeId<T>();
+ if (T t; QJSEngine::convertManaged(value, QMetaType::fromType<T>(), &t))
+ return t;
- if (qjsvalue_cast_helper(value, id, &t))
+ return qvariant_cast<T>(value.toVariant());
+}
+
+template<typename T>
+T qjsvalue_cast(const QJSPrimitiveValue &value)
+{
+ if (T t; QJSEngine::convertPrimitive(value, QMetaType::fromType<T>(), &t))
return t;
- else if (value.isVariant())
- return qvariant_cast<T>(value.toVariant());
- return T();
+ return qvariant_cast<T>(value.toVariant());
}
template <>
@@ -164,6 +386,18 @@ inline QVariant qjsvalue_cast<QVariant>(const QJSValue &value)
return value.toVariant();
}
+template <>
+inline QVariant qjsvalue_cast<QVariant>(const QJSManagedValue &value)
+{
+ return value.toVariant();
+}
+
+template <>
+inline QVariant qjsvalue_cast<QVariant>(const QJSPrimitiveValue &value)
+{
+ return value.toVariant();
+}
+
Q_QML_EXPORT QJSEngine *qjsEngine(const QObject *);
QT_END_NAMESPACE
diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h
index 164a70d000..735dae8d81 100644
--- a/src/qml/jsapi/qjsengine_p.h
+++ b/src/qml/jsapi/qjsengine_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QJSENGINE_P_H
#define QJSENGINE_P_H
@@ -53,6 +17,7 @@
#include <QtCore/private/qobject_p.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qproperty.h>
#include "qjsengine.h"
#include "private/qtqmlglobal_p.h"
#include <private/qqmlmetatype_p.h>
@@ -65,7 +30,7 @@ namespace QV4 {
struct ExecutionEngine;
}
-class Q_QML_PRIVATE_EXPORT QJSEnginePrivate : public QObjectPrivate
+class Q_QML_EXPORT QJSEnginePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QJSEngine)
@@ -80,114 +45,10 @@ public:
static void addToDebugServer(QJSEngine *q);
static void removeFromDebugServer(QJSEngine *q);
- // Locker locks the QQmlEnginePrivate data structures for read and write, if necessary.
- // Currently, locking is only necessary if the threaded loader is running concurrently. If it is
- // either idle, or is running with the main thread blocked, no locking is necessary. This way
- // we only pay for locking when we have to.
- // Consequently, this class should only be used to protect simple accesses or modifications of the
- // QQmlEnginePrivate structures or operations that can be guaranteed not to start activity
- // on the loader thread.
- // The Locker API is identical to QMutexLocker. Locker reuses the QQmlEnginePrivate::mutex
- // QMutex instance and multiple Lockers are recursive in the same thread.
- class Locker
- {
- public:
- inline Locker(const QJSEngine *);
- inline Locker(const QJSEnginePrivate *);
- inline ~Locker();
-
- inline void unlock();
- inline void relock();
-
- private:
- const QJSEnginePrivate *m_ep;
- quint32 m_locked:1;
- };
-
- // Shared by QQmlEngine
- mutable QRecursiveMutex mutex;
-
-
- // These methods may be called from the QML loader thread
- inline QQmlPropertyCache *cache(QObject *obj, int minorVersion = -1);
- inline QQmlPropertyCache *cache(const QMetaObject *, int minorVersion = -1);
+ void uiLanguageChanged() { Q_Q(QJSEngine); if (q) q->uiLanguageChanged(); }
+ Q_OBJECT_BINDABLE_PROPERTY(QJSEnginePrivate, QString, uiLanguage, &QJSEnginePrivate::uiLanguageChanged);
};
-QJSEnginePrivate::Locker::Locker(const QJSEngine *e)
-: m_ep(QJSEnginePrivate::get(e))
-{
- relock();
-}
-
-QJSEnginePrivate::Locker::Locker(const QJSEnginePrivate *e)
-: m_ep(e), m_locked(false)
-{
- relock();
-}
-
-QJSEnginePrivate::Locker::~Locker()
-{
- unlock();
-}
-
-void QJSEnginePrivate::Locker::unlock()
-{
- if (m_locked) {
- m_ep->mutex.unlock();
- m_locked = false;
- }
-}
-
-void QJSEnginePrivate::Locker::relock()
-{
- Q_ASSERT(!m_locked);
- m_ep->mutex.lock();
- m_locked = true;
-}
-
-/*!
-Returns a QQmlPropertyCache for \a obj if one is available.
-
-If \a obj is null, being deleted or contains a dynamic meta object 0
-is returned.
-
-The returned cache is not referenced, so if it is to be stored, call addref().
-
-XXX thread There is a potential future race condition in this and all the cache()
-functions. As the QQmlPropertyCache is returned unreferenced, when called
-from the loader thread, it is possible that the cache will have been dereferenced
-and deleted before the loader thread has a chance to use or reference it. This
-can't currently happen as the cache holds a reference to the
-QQmlPropertyCache until the QQmlEngine is destroyed.
-*/
-QQmlPropertyCache *QJSEnginePrivate::cache(QObject *obj, int minorVersion)
-{
- if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted)
- return nullptr;
-
- Locker locker(this);
- const QMetaObject *mo = obj->metaObject();
- return QQmlMetaType::propertyCache(mo, minorVersion);
-}
-
-/*!
-Returns a QQmlPropertyCache for \a metaObject.
-
-As the cache is persisted for the life of the engine, \a metaObject must be
-a static "compile time" meta-object, or a meta-object that is otherwise known to
-exist for the lifetime of the QQmlEngine.
-
-The returned cache is not referenced, so if it is to be stored, call addref().
-*/
-QQmlPropertyCache *QJSEnginePrivate::cache(const QMetaObject *metaObject, int minorVersion)
-{
- Q_ASSERT(metaObject);
-
- Locker locker(this);
- return QQmlMetaType::propertyCache(metaObject, minorVersion);
-}
-
-
QT_END_NAMESPACE
#endif // QJSENGINE_P_H
diff --git a/src/qml/jsapi/qjslist.cpp b/src/qml/jsapi/qjslist.cpp
new file mode 100644
index 0000000000..b3a4eed3c0
--- /dev/null
+++ b/src/qml/jsapi/qjslist.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtQml/qjslist.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \class QJSListIndexClamp
+ * \internal
+ */
+
+/*!
+ * \class QJSList
+ * \internal
+ */
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsapi/qjslist.h b/src/qml/jsapi/qjslist.h
new file mode 100644
index 0000000000..d604e266f2
--- /dev/null
+++ b/src/qml/jsapi/qjslist.h
@@ -0,0 +1,368 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJSLIST_H
+#define QJSLIST_H
+
+#include <QtQml/qtqmlglobal.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qjsengine.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+#include <algorithm>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version. It will be kept compatible with the intended usage by
+// code generated using qmlcachegen.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+struct QJSListIndexClamp
+{
+ static qsizetype clamp(qsizetype start, qsizetype max, qsizetype min = 0)
+ {
+ Q_ASSERT(min >= 0);
+ Q_ASSERT(min <= max);
+ return std::clamp(start < 0 ? max + qsizetype(start) : qsizetype(start), min, max);
+ }
+};
+
+template<typename List, typename Value = typename List::value_type>
+struct QJSList : private QJSListIndexClamp
+{
+ Q_DISABLE_COPY_MOVE(QJSList)
+
+ QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {}
+
+ Value at(qsizetype index) const
+ {
+ Q_ASSERT(index >= 0 && index < size());
+ return *(m_list->cbegin() + index);
+ }
+
+ qsizetype size() const { return m_list->size(); }
+
+ void resize(qsizetype size)
+ {
+ m_list->resize(size);
+ }
+
+ bool includes(const Value &value) const
+ {
+ return std::find(m_list->cbegin(), m_list->cend(), value) != m_list->cend();
+ }
+
+ bool includes(const Value &value, qsizetype start) const
+ {
+ return std::find(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), value)
+ != m_list->cend();
+ }
+
+ QString join(const QString &separator = QStringLiteral(",")) const
+ {
+ QString result;
+ bool atBegin = true;
+ std::for_each(m_list->cbegin(), m_list->cend(), [&](const Value &value) {
+ if (atBegin)
+ atBegin = false;
+ else
+ result += separator;
+ result += m_engine->coerceValue<Value, QString>(value);
+ });
+ return result;
+ }
+
+ List slice() const
+ {
+ return *m_list;
+ }
+ List slice(qsizetype start) const
+ {
+ List result;
+ std::copy(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(),
+ std::back_inserter(result));
+ return result;
+ }
+ List slice(qsizetype start, qsizetype end) const
+ {
+ const qsizetype size = m_list->size();
+ const qsizetype clampedStart = clamp(start, size);
+ const qsizetype clampedEnd = clamp(end, size, clampedStart);
+
+ List result;
+ std::copy(m_list->cbegin() + clampedStart, m_list->cbegin() + clampedEnd,
+ std::back_inserter(result));
+ return result;
+ }
+
+ qsizetype indexOf(const Value &value) const
+ {
+ const auto begin = m_list->cbegin();
+ const auto end = m_list->cend();
+ const auto it = std::find(begin, end, value);
+ if (it == end)
+ return -1;
+ const qsizetype result = it - begin;
+ Q_ASSERT(result >= 0);
+ return result;
+ }
+ qsizetype indexOf(const Value &value, qsizetype start) const
+ {
+ const auto begin = m_list->cbegin();
+ const auto end = m_list->cend();
+ const auto it = std::find(begin + clamp(start, m_list->size()), end, value);
+ if (it == end)
+ return -1;
+ const qsizetype result = it - begin;
+ Q_ASSERT(result >= 0);
+ return result;
+ }
+
+ qsizetype lastIndexOf(const Value &value) const
+ {
+ const auto begin = std::make_reverse_iterator(m_list->cend());
+ const auto end = std::make_reverse_iterator(m_list->cbegin());
+ const auto it = std::find(begin, end, value);
+ return (end - it) - 1;
+ }
+ qsizetype lastIndexOf(const Value &value, qsizetype start) const
+ {
+ const qsizetype size = m_list->size();
+ if (size == 0)
+ return -1;
+
+ // Construct a one-past-end iterator as input.
+ const qsizetype clampedStart = std::min(clamp(start, size), size - 1);
+ const auto begin = std::make_reverse_iterator(m_list->cbegin() + clampedStart + 1);
+
+ const auto end = std::make_reverse_iterator(m_list->cbegin());
+ const auto it = std::find(begin, end, value);
+ return (end - it) - 1;
+ }
+
+ QString toString() const { return join(); }
+
+private:
+ List *m_list = nullptr;
+ QJSEngine *m_engine = nullptr;
+};
+
+template<>
+struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClamp
+{
+ Q_DISABLE_COPY_MOVE(QJSList)
+
+ QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {}
+
+ QObject *at(qsizetype index) const
+ {
+ Q_ASSERT(index >= 0 && index < size());
+ return m_list->at(m_list, index);
+ }
+
+ qsizetype size() const
+ {
+ return m_list->count(m_list);
+ }
+
+ void resize(qsizetype size)
+ {
+ qsizetype current = m_list->count(m_list);
+ if (current < size && m_list->append) {
+ do {
+ m_list->append(m_list, nullptr);
+ } while (++current < size);
+ } else if (current > size && m_list->removeLast) {
+ do {
+ m_list->removeLast(m_list);
+ } while (--current > size);
+ }
+ }
+
+ bool includes(const QObject *value) const
+ {
+ if (!m_list->count || !m_list->at)
+ return false;
+
+ const qsizetype size = m_list->count(m_list);
+ for (qsizetype i = 0; i < size; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return true;
+ }
+
+ return false;
+ }
+ bool includes(const QObject *value, qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return false;
+
+ const qsizetype size = m_list->count(m_list);
+ for (qsizetype i = clamp(start, size); i < size; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return true;
+ }
+
+ return false;
+ }
+
+ QString join(const QString &separator = QStringLiteral(",")) const
+ {
+ if (!m_list->count || !m_list->at)
+ return QString();
+
+ QString result;
+ for (qsizetype i = 0, end = m_list->count(m_list); i < end; ++i) {
+ if (i != 0)
+ result += separator;
+ result += m_engine->coerceValue<QObject *, QString>(m_list->at(m_list, i));
+ }
+
+ return result;
+ }
+
+ QObjectList slice() const
+ {
+ return m_list->toList<QObjectList>();
+ }
+ QObjectList slice(qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return QObjectList();
+
+ const qsizetype size = m_list->count(m_list);
+ const qsizetype clampedStart = clamp(start, size);
+ QObjectList result;
+ result.reserve(size - clampedStart);
+ for (qsizetype i = clampedStart; i < size; ++i)
+ result.append(m_list->at(m_list, i));
+ return result;
+ }
+ QObjectList slice(qsizetype start, qsizetype end) const
+ {
+ if (!m_list->count || !m_list->at)
+ return QObjectList();
+
+ const qsizetype size = m_list->count(m_list);
+ const qsizetype clampedStart = clamp(start, size);
+ const qsizetype clampedEnd = clamp(end, size, clampedStart);
+ QObjectList result;
+ result.reserve(clampedEnd - clampedStart);
+ for (qsizetype i = clampedStart; i < clampedEnd; ++i)
+ result.append(m_list->at(m_list, i));
+ return result;
+ }
+
+ qsizetype indexOf(const QObject *value) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ const qsizetype end = m_list->count(m_list);
+ for (qsizetype i = 0; i < end; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+ qsizetype indexOf(const QObject *value, qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ const qsizetype size = m_list->count(m_list);
+ for (qsizetype i = clamp(start, size); i < size; ++i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+
+ qsizetype lastIndexOf(const QObject *value) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ for (qsizetype i = m_list->count(m_list) - 1; i >= 0; --i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+ qsizetype lastIndexOf(const QObject *value, qsizetype start) const
+ {
+ if (!m_list->count || !m_list->at)
+ return -1;
+
+ const qsizetype size = m_list->count(m_list);
+ if (size == 0)
+ return -1;
+
+ qsizetype clampedStart = std::min(clamp(start, size), size - 1);
+ for (qsizetype i = clampedStart; i >= 0; --i) {
+ if (m_list->at(m_list, i) == value)
+ return i;
+ }
+ return -1;
+ }
+
+ QString toString() const { return join(); }
+
+private:
+ QQmlListProperty<QObject> *m_list = nullptr;
+ QJSEngine *m_engine = nullptr;
+};
+
+struct QJSListForInIterator
+{
+public:
+ using Ptr = QJSListForInIterator *;
+ template<typename List, typename Value>
+ void init(const QJSList<List, Value> &list)
+ {
+ m_index = 0;
+ m_size = list.size();
+ }
+
+ bool hasNext() const { return m_index < m_size; }
+ qsizetype next() { return m_index++; }
+
+private:
+ qsizetype m_index;
+ qsizetype m_size;
+};
+
+// QJSListForInIterator must not require initialization so that we can jump over it with goto.
+static_assert(std::is_trivial_v<QJSListForInIterator>);
+
+struct QJSListForOfIterator
+{
+public:
+ using Ptr = QJSListForOfIterator *;
+ void init() { m_index = 0; }
+
+ template<typename List, typename Value>
+ bool hasNext(const QJSList<List, Value> &list) const { return m_index < list.size(); }
+
+ template<typename List, typename Value>
+ Value next(const QJSList<List, Value> &list) { return list.at(m_index++); }
+
+private:
+ qsizetype m_index;
+};
+
+// QJSListForOfIterator must not require initialization so that we can jump over it with goto.
+static_assert(std::is_trivial_v<QJSListForOfIterator>);
+
+QT_END_NAMESPACE
+
+#endif // QJSLIST_H
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp
new file mode 100644
index 0000000000..452f991a26
--- /dev/null
+++ b/src/qml/jsapi/qjsmanagedvalue.cpp
@@ -0,0 +1,1145 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtQml/qjsmanagedvalue.h>
+#include <QtQml/qjsengine.h>
+#include <QtQml/private/qv4persistent_p.h>
+#include <QtQml/private/qv4engine_p.h>
+#include <QtQml/private/qv4mm_p.h>
+#include <QtQml/private/qjsvalue_p.h>
+#include <QtQml/private/qv4runtime_p.h>
+#include <QtQml/private/qv4functionobject_p.h>
+#include <QtQml/private/qv4jscall_p.h>
+#include <QtQml/private/qv4urlobject_p.h>
+#include <QtQml/private/qv4variantobject_p.h>
+#include <QtQml/private/qv4qobjectwrapper_p.h>
+#include <QtQml/private/qv4qmetaobjectwrapper_p.h>
+#include <QtQml/private/qv4regexpobject_p.h>
+#include <QtQml/private/qv4dateobject_p.h>
+#include <QtQml/private/qv4errorobject_p.h>
+#include <QtQml/private/qv4identifiertable_p.h>
+
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \class QJSManagedValue
+ * \inmodule QtQml
+ * \since 6.1
+ *
+ * \inmodule QtQml
+ *
+ * \brief QJSManagedValue represents a value on the JavaScript heap belonging to a QJSEngine.
+ *
+ * The QJSManagedValue class allows interaction with JavaScript values in most
+ * ways you can interact with them from JavaScript itself. You can get and set
+ * properties and prototypes, and you can access arrays. Additionally, you can
+ * transform the value into the Qt counterparts of JavaScript objects. For
+ * example, a Url object may be transformed into a QUrl.
+ *
+ * A QJSManagedValue is always bound to a particular QJSEngine. You cannot use
+ * it independently. This means that you cannot have a QJSManagedValue from one
+ * engine be a property or a proptotype of a QJSManagedValue from a different
+ * engine.
+ *
+ * In contrast to QJSValue, almost all values held by QJSManagedValue live on
+ * the JavaScript heap. There is no inline or unmanaged storage. Therefore, you
+ * can get the prototype of a primitive value, and you can get the \c length
+ * property of a string.
+ *
+ * Only default-constructed or moved-from QJSManagedValues do not hold a value
+ * on the JavaScript heap. They represent \c undefined, which doesn't have any
+ * properties or prototypes.
+ *
+ * Also in contrast to QJSValue, QJSManagedValue does not catch any JavaScript
+ * exceptions. If an operation on a QJSManagedValue causes an error, it will
+ * generally return an \c undefined value and QJSEngine::hasError() will return
+ * \c true afterwards. You can then catch the exception using
+ * QJSEngine::catchError(), or pass it up the stack, at your own discretion.
+ *
+ * \note As the reference to the value on the JavaScript heap has to be freed
+ * on destruction, you cannot move a QJSManagedValue to a different thread.
+ * The destruction would take place in the new thread, which would create a race
+ * condition with the garbage collector on the original thread. This also means
+ * that you cannot hold a QJSManagedValue beyond the lifespan of its engine.
+ *
+ * The recommended way of working with a QJSManagedValue is creating it
+ * on the stack, possibly by moving a QJSValue and adding an engine, then
+ * performing the necessary operations on it, and finally moving it back into a
+ * QJSValue for storage. Moving between QJSManagedValue and QJSValue is fast.
+ */
+
+/*!
+ * \enum QJSManagedValue::Type
+ *
+ * This enum represents the JavaScript native types, as specified by
+ * \l{ECMA-262}.
+ *
+ * \value Undefined The \c undefined type
+ * \value Boolean The \c boolean type
+ * \value Number The \c number type
+ * \value String The \c string type
+ * \value Object The \c object type
+ * \value Symbol The \c symbol type
+ * \value Function The \c function type
+ *
+ * Note that the \c null value is not a type of itself but rather a special kind
+ * of object. You can query a QJSManagedValue for this condition using the
+ * isNull() method. Furthermore, JavaScript has no integer type, but it knows a
+ * special treatment of numbers in preparation for integer only operations. You
+ * can query a QJSManagedValue to find out whether it holds the result of such a
+ * treatment by using the isInteger() method.
+ */
+
+/*!
+ * \fn QJSManagedValue::QJSManagedValue()
+ *
+ * Creates a QJSManagedValue that represents the JavaScript \c undefined value.
+ * This is the only value not stored on the JavaScript heap. Calling engine()
+ * on a default-constructed QJSManagedValue will return nullptr.
+ */
+
+static QV4::ExecutionEngine *v4Engine(QV4::Value *d)
+{
+ if (!d)
+ return nullptr;
+
+ QV4::ExecutionEngine *v4 = QV4::PersistentValueStorage::getEngine(d);
+ Q_ASSERT(v4);
+ return v4;
+}
+
+/*!
+ * Creates a QJSManagedValue from \a value, using the heap of \a engine. If
+ * \a value is itself managed and the engine it belongs to is not \a engine,
+ * the result is an \c undefined value, and a warning is generated.
+ */
+QJSManagedValue::QJSManagedValue(QJSValue value, QJSEngine *engine)
+{
+ QV4::ExecutionEngine *v4 = engine->handle();
+
+ if (QV4::Value *m = QJSValuePrivate::takeManagedValue(&value)) {
+ if (Q_UNLIKELY(v4Engine(m) != v4)) {
+ qWarning("QJSManagedValue(QJSValue, QJSEngine *) failed: "
+ "Value was created in different engine.");
+ QV4::PersistentValueStorage::free(m);
+ return;
+ }
+
+ d = m;
+ return;
+ }
+
+ d = v4->memoryManager->m_persistentValues->allocate();
+
+ if (const QString *string = QJSValuePrivate::asQString(&value))
+ *d = v4->newString(*string);
+ else
+ *d = QJSValuePrivate::asReturnedValue(&value);
+}
+
+/*!
+ * Creates a QJSManagedValue from \a value using the heap of \a engine.
+ */
+QJSManagedValue::QJSManagedValue(const QJSPrimitiveValue &value, QJSEngine *engine) :
+ QJSManagedValue(engine->handle())
+{
+ switch (value.type()) {
+ case QJSPrimitiveValue::Undefined:
+ *d = QV4::Encode::undefined();
+ return;
+ case QJSPrimitiveValue::Null:
+ *d = QV4::Encode::null();
+ return;
+ case QJSPrimitiveValue::Boolean:
+ *d = QV4::Encode(value.asBoolean());
+ return;
+ case QJSPrimitiveValue::Integer:
+ *d = QV4::Encode(value.asInteger());
+ return;
+ case QJSPrimitiveValue::Double:
+ *d = QV4::Encode(value.asDouble());
+ return;
+ case QJSPrimitiveValue::String:
+ *d = engine->handle()->newString(value.asString());
+ return;
+ }
+
+ Q_UNREACHABLE();
+}
+
+/*!
+ * Creates a QJSManagedValue from \a variant using the heap of \a engine.
+ */
+QJSManagedValue::QJSManagedValue(const QVariant &variant, QJSEngine *engine) :
+ QJSManagedValue(engine->handle())
+{
+ *d = engine->handle()->fromVariant(variant);
+}
+
+/*!
+ * Creates a QJSManagedValue from \a string using the heap of \a engine.
+ */
+QJSManagedValue::QJSManagedValue(const QString &string, QJSEngine *engine) :
+ QJSManagedValue(engine->handle())
+{
+ *d = engine->handle()->newString(string);
+}
+
+/*!
+ * Destroys the QJSManagedValue.
+ *
+ * \note This frees the memory slot it holds on the JavaScript heap. You must
+ * not destroy a QJSManagedValue from a different thread than the one
+ * where the QJSEngine it belongs to lives.
+ */
+QJSManagedValue::~QJSManagedValue()
+{
+ QV4::PersistentValueStorage::free(d);
+}
+
+/*!
+ * Move-constructs a QJSManagedValue from \a other. This leaves \a other in
+ * the default-constructed state where it represents undefined and does not
+ * belong to any engine.
+ */
+QJSManagedValue::QJSManagedValue(QJSManagedValue &&other)
+{
+ qSwap(d, other.d);
+}
+
+/*!
+ * Move-assigns a QJSManagedValue from \a other. This leaves \a other in
+ * the default-constructed state where it represents undefined and does not
+ * belong to any engine.
+ *
+ * \note This frees the memory slot this QJSManagedValue holds on the
+ * JavaScript heap. You must not move-assign a QJSManagedValue on a
+ * different thread than the one where the QJSEngine it belongs to lives.
+ */
+QJSManagedValue &QJSManagedValue::operator=(QJSManagedValue &&other)
+{
+ if (this != &other) {
+ QV4::PersistentValueStorage::free(d);
+ d = nullptr;
+ qSwap(d, other.d);
+ }
+ return *this;
+}
+
+/*!
+ * Invokes the JavaScript '==' operator on this QJSManagedValue and \a other,
+ * and returns the result.
+ *
+ * \sa strictlyEquals
+ */
+bool QJSManagedValue::equals(const QJSManagedValue &other) const
+{
+ if (!d)
+ return !other.d || other.d->isNullOrUndefined();
+ if (!other.d)
+ return d->isNullOrUndefined();
+
+ return QV4::Runtime::CompareEqual::call(*d, *other.d);
+}
+
+/*!
+ * Invokes the JavaScript '===' operator on this QJSManagedValue and \a other,
+ * and returns the result.
+ *
+ * \sa equals
+ */
+bool QJSManagedValue::strictlyEquals(const QJSManagedValue &other) const
+{
+ if (!d)
+ return !other.d || other.d->isUndefined();
+ if (!other.d)
+ return d->isUndefined();
+
+ return QV4::RuntimeHelpers::strictEqual(*d, *other.d);
+}
+
+/*!
+ * Returns the QJSEngine this QJSManagedValue belongs to. Mind that the engine
+ * is always valid, unless the QJSManagedValue is default-constructed or moved
+ * from. In the latter case a nullptr is returned.
+ */
+QJSEngine *QJSManagedValue::engine() const
+{
+ if (!d)
+ return nullptr;
+ if (QV4::ExecutionEngine *v4 = QV4::PersistentValueStorage::getEngine(d))
+ return v4->jsEngine();
+ return nullptr;
+}
+
+/*!
+ * Returns the prototype for this QJSManagedValue. This works on any value. You
+ * can, for example retrieve the JavaScript \c boolean prototype from a \c boolean
+ * value.
+ */
+QJSManagedValue QJSManagedValue::prototype() const
+{
+ if (!d)
+ return QJSManagedValue();
+
+ QV4::ExecutionEngine *v4 = v4Engine(d);
+ QJSManagedValue result(v4);
+
+ if (auto object = d->as<QV4::Object>())
+ *result.d = object->getPrototypeOf();
+ else if (auto managed = d->as<QV4::Managed>())
+ *result.d = managed->internalClass()->prototype;
+ else if (d->isBoolean())
+ *result.d = v4->booleanPrototype();
+ else if (d->isNumber())
+ *result.d = v4->numberPrototype();
+
+ // If the prototype appears to be undefined, then it's actually null in JS terms.
+ if (result.d->isUndefined())
+ *result.d = QV4::Encode::null();
+
+ return result;
+}
+
+/*!
+ * Sets the prototype of this QJSManagedValue to \a prototype. A precondition
+ * is that \a prototype belongs to the same QJSEngine as this QJSManagedValue
+ * and is an object (including null). Furthermore, this QJSManagedValue has to
+ * be an object (excluding null), too, and you cannot create prototype cycles.
+ */
+void QJSManagedValue::setPrototype(const QJSManagedValue &prototype)
+{
+ auto object = d ? d->as<QV4::Object>() : nullptr;
+ if (!object) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Can only set a prototype on an object (excluding null).");
+ return;
+ }
+
+ // Object includes null ...
+ if (prototype.type() != QJSManagedValue::Object) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Can only set objects (including null) as prototypes.");
+ return;
+ }
+
+ if (Q_UNLIKELY(object->engine() != v4Engine(prototype.d))) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Prototype was created in differen engine.");
+ return;
+ }
+
+ // ... Null becomes nullptr here. That is why it appears as undefined later.
+ if (!object->setPrototypeOf(prototype.d->as<QV4::Object>())) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Prototype cycle detected.");
+ }
+}
+
+/*!
+ * Returns the JavaScript type of this QJSManagedValue.
+ */
+QJSManagedValue::Type QJSManagedValue::type() const
+{
+ if (!d || d->isUndefined())
+ return Undefined;
+ if (d->isBoolean())
+ return Boolean;
+ if (d->isNumber())
+ return Number;
+ if (d->isString())
+ return String;
+ if (d->isSymbol())
+ return Symbol;
+ if (d->isFunctionObject())
+ return Function;
+ return Object;
+}
+
+/*!
+ * \fn QJSManagedValue::isUndefined() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c undefined,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isBoolean() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c boolean,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isNumber() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c number,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isString() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c string,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isSymbol() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c symbol,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isObject() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c object,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isFunction() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c function,
+ * \c false otherwise.
+ */
+
+/*!
+ * Returns \c true if this QJSManagedValue holds the JavaScript \c null value,
+ * or \c false otherwise.
+ */
+bool QJSManagedValue::isNull() const
+{
+ return d && d->isNull();
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue holds an integer value, or \c false
+ * otherwise. The storage format of a number does not affect the result of any
+ * operations performed on it, but if an integer is stored, many operations are
+ * faster.
+ */
+bool QJSManagedValue::isInteger() const
+{
+ return d && d->isInteger();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript regular expression
+ * object, or \c false otherwise.
+ */
+bool QJSManagedValue::isRegularExpression() const
+{
+ return d && d->as<QV4::RegExpObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Array
+ * object, or \c false otherwise.
+ */
+bool QJSManagedValue::isArray() const
+{
+ return d && d->as<QV4::ArrayObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Url
+ * object, or \c false otherwise.
+ */
+bool QJSManagedValue::isUrl() const
+{
+ return d && d->as<QV4::UrlObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a QVariant managed on the JavaScript
+ * heap, or \c false otherwise.
+ */
+bool QJSManagedValue::isVariant() const
+{
+ return d && d->as<QV4::VariantObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a QObject pointer managed on the
+ * JavaScript heap, or \c false otherwise.
+ */
+bool QJSManagedValue::isQObject() const
+{
+ return d && d->as<QV4::QObjectWrapper>();
+}
+
+/*!
+ * Returns \c true if this value represents a QMetaObject pointer managed on the
+ * JavaScript heap, or \c false otherwise.
+ */
+bool QJSManagedValue::isQMetaObject() const
+{
+ return d && d->as<QV4::QMetaObjectWrapper>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Date object, or
+ * \c false otherwise.
+ */
+bool QJSManagedValue::isDate() const
+{
+ return d && d->as<QV4::DateObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Error object, or
+ * \c false otherwise.
+ */
+bool QJSManagedValue::isError() const
+{
+ return d && d->as<QV4::ErrorObject>();
+}
+
+/*!
+ * \internal
+ *
+ * Returns \c true if this value represents a JavaScript meta type, or \c false
+ * otherwise.
+ */
+bool QJSManagedValue::isJsMetaType() const
+{
+ return d && d->as<QV4::InternalClass>();
+}
+
+/*!
+ * Converts the manged value to a string. If the managed value holds a string,
+ * that one is returned. Otherwise a string coercion by JavaScript rules is
+ * performed.
+ *
+ * \note Conversion of a managed value to a string can throw an exception. In
+ * particular, symbols cannot be coerced into strings, or a custom
+ * toString() method may throw. In this case the result is an empty
+ * string and the engine carries an error after the conversion.
+ */
+QString QJSManagedValue::toString() const
+{
+ return d ? d->toQString() : QStringLiteral("undefined");
+}
+
+/*!
+ * Converts the manged value to a number. If the managed value holds a number,
+ * that one is returned. Otherwise a number coercion by JavaScript rules is
+ * performed.
+ *
+ * \note Conversion of a managed value to a number can throw an exception. In
+ * particular, symbols cannot be coerced into numbers, or a custom
+ * valueOf() method may throw. In this case the result is 0 and the
+ * engine carries an error after the conversion.
+ */
+double QJSManagedValue::toNumber() const
+{
+ return d ? d->toNumber() : 0;
+}
+
+/*!
+ * Converts the manged value to a boolean. If the managed value holds a boolean,
+ * that one is returned. Otherwise a boolean coercion by JavaScript rules is
+ * performed.
+ */
+bool QJSManagedValue::toBoolean() const
+{
+ return d ? d->toBoolean() : false;
+}
+
+/*!
+ * Converts the manged value to an integer. This first converts the value to a
+ * number by the rules of toNumber(), and then clamps it into the integer range
+ * by the rules given for coercing the arguments to JavaScript bit shift
+ * operators into 32bit integers.
+ *
+ * Internally, the value may already be stored as an integer, in which case a
+ * fast path is taken.
+ *
+ * \note Conversion of a managed value to a number can throw an exception. In
+ * particular, symbols cannot be coerced into numbers, or a custom
+ * valueOf() method may throw. In this case the result is 0 and the
+ * engine carries an error after the conversion.
+ *
+ * \note The JavaScript rules for coercing numbers into 32bit integers are
+ * unintuitive.
+ */
+int QJSManagedValue::toInteger() const
+{
+ return d ? d->toInt32() : 0;
+}
+
+/*!
+ * Converts the manged value to a QJSPrimitiveValue. If the managed value holds
+ * a type supported by QJSPrimitiveValue, the value is copied. Otherwise the
+ * value is converted to a string, and the string is stored in
+ * QJSPrimitiveValue.
+ *
+ * \note Conversion of a managed value to a string can throw an exception. In
+ * particular, symbols cannot be coerced into strings, or a custom
+ * toString() method may throw. In this case the result is the undefined
+ * value and the engine carries an error after the conversion.
+ */
+QJSPrimitiveValue QJSManagedValue::toPrimitive() const
+{
+ if (!d || d->isUndefined())
+ return QJSPrimitiveUndefined();
+ if (d->isNull())
+ return QJSPrimitiveNull();
+ if (d->isBoolean())
+ return d->booleanValue();
+ if (d->isInteger())
+ return d->integerValue();
+ if (d->isNumber())
+ return d->doubleValue();
+
+ bool ok;
+ const QString result = d->toQString(&ok);
+ return ok ? QJSPrimitiveValue(result) : QJSPrimitiveValue(QJSPrimitiveUndefined());
+}
+
+/*!
+ * Copies this QJSManagedValue into a new QJSValue. This is less efficient than
+ * move-constructing a QJSValue from a QJSManagedValue, but retains the
+ * QJSManagedValue.
+ */
+QJSValue QJSManagedValue::toJSValue() const
+{
+ return d ? QJSValuePrivate::fromReturnedValue(d->asReturnedValue()) : QJSValue();
+}
+
+/*!
+ * Copies this QJSManagedValue into a new QVariant. This also creates a useful
+ * QVariant if QJSManagedValue::isVariant() returns false. QVariant can hold all
+ * types supported by QJSManagedValue.
+ */
+QVariant QJSManagedValue::toVariant() const
+{
+ if (!d || d->isUndefined())
+ return QVariant();
+ if (d->isNull())
+ return QVariant(QMetaType::fromType<std::nullptr_t>(), nullptr);
+ if (d->isBoolean())
+ return QVariant(d->booleanValue());
+ if (d->isInteger())
+ return QVariant(d->integerValue());
+ if (d->isNumber())
+ return QVariant(d->doubleValue());
+ if (d->isString())
+ return QVariant(d->toQString());
+ if (d->as<QV4::Managed>())
+ return QV4::ExecutionEngine::toVariant(*d, QMetaType{}, true);
+
+ Q_UNREACHABLE_RETURN(QVariant());
+}
+
+/*!
+ * If this QJSManagedValue holds a JavaScript regular expression object, returns
+ * an equivalent QRegularExpression. Otherwise returns an invalid one.
+ */
+QRegularExpression QJSManagedValue::toRegularExpression() const
+{
+ if (const auto *r = d ? d->as<QV4::RegExpObject>() : nullptr)
+ return r->toQRegularExpression();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a JavaScript Url object, returns
+ * an equivalent QUrl. Otherwise returns an invalid one.
+ */
+QUrl QJSManagedValue::toUrl() const
+{
+ if (const auto *u = d ? d->as<QV4::UrlObject>() : nullptr)
+ return u->toQUrl();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a QObject pointer, returns it. Otherwise
+ * returns nullptr.
+ */
+QObject *QJSManagedValue::toQObject() const
+{
+ if (const auto *o = d ? d->as<QV4::QObjectWrapper>() : nullptr)
+ return o->object();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a QMetaObject pointer, returns it.
+ * Otherwise returns nullptr.
+ */
+const QMetaObject *QJSManagedValue::toQMetaObject() const
+{
+ if (const auto *m = d ? d->as<QV4::QMetaObjectWrapper>() : nullptr)
+ return m->metaObject();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a JavaScript Date object, returns an equivalent
+ * QDateTime. Otherwise returns an invalid one.
+ */
+QDateTime QJSManagedValue::toDateTime() const
+{
+ if (const auto *t = d ? d->as<QV4::DateObject>() : nullptr)
+ return t->toQDateTime();
+ return {};
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has a property \a name, otherwise
+ * returns \c false. The properties of the prototype chain are considered.
+ */
+bool QJSManagedValue::hasProperty(const QString &name) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (d->isString() && name == QStringLiteral("length"))
+ return true;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
+ return obj->hasProperty(key);
+ }
+
+ return prototype().hasProperty(name);
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has a property \a name, otherwise
+ * returns \c false. The properties of the prototype chain are not considered.
+ */
+bool QJSManagedValue::hasOwnProperty(const QString &name) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (d->isString() && name == QStringLiteral("length"))
+ return true;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
+ return obj->getOwnProperty(key) != QV4::Attr_Invalid;
+ }
+
+ return false;
+}
+
+/*!
+ * Returns the property \a name of this QJSManagedValue. The prototype chain
+ * is searched if the property is not found on the actual object.
+ */
+QJSValue QJSManagedValue::property(const QString &name) const
+{
+ if (!d)
+ return QJSValue();
+
+ if (d->isNullOrUndefined()) {
+ QV4::ExecutionEngine *e = v4Engine(d);
+ e->throwTypeError(QStringLiteral("Cannot read property '%1' of null").arg(name));
+ return QJSValue();
+ }
+
+ if (QV4::String *string = d->as<QV4::String>()) {
+ if (name == QStringLiteral("length"))
+ return QJSValue(string->d()->length());
+ }
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
+ return QJSValuePrivate::fromReturnedValue(obj->get(key));
+ }
+
+ return prototype().property(name);
+}
+
+/*!
+ * Sets the property \a name to \a value on this QJSManagedValue. This can only
+ * be done on JavaScript values of type \c object. Furhermore, \a value has to be
+ * either a primitive or belong to the same engine as this value.
+ */
+void QJSManagedValue::setProperty(const QString &name, const QJSValue &value)
+{
+ if (!d)
+ return;
+
+ if (d->isNullOrUndefined()) {
+ v4Engine(d)->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ }
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
+ if (Q_UNLIKELY(v4 && v4 != scope.engine)) {
+ qWarning("QJSManagedValue::setProperty() failed: "
+ "Value was created in different engine.");
+ return;
+ }
+ QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
+ obj->put(key, QJSValuePrivate::convertToReturnedValue(scope.engine, value));
+ }
+}
+
+/*!
+ * Deletes the property \a name from this QJSManagedValue. Returns \c true if
+ * the deletion succeeded, or \c false otherwise.
+ */
+bool QJSManagedValue::deleteProperty(const QString &name)
+{
+ if (!d)
+ return false;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
+ return obj->deleteProperty(key);
+ }
+
+ return false;
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has an array index \a arrayIndex,
+ * otherwise returns \c false. The properties of the prototype chain are
+ * considered.
+ */
+bool QJSManagedValue::hasProperty(quint32 arrayIndex) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (QV4::String *string = d->as<QV4::String>())
+ return arrayIndex < quint32(string->d()->length());
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ bool hasProperty = false;
+ if (arrayIndex == std::numeric_limits<quint32>::max())
+ obj->get(obj->engine()->id_uintMax(), &hasProperty);
+ else
+ obj->get(arrayIndex, &hasProperty);
+ return hasProperty;
+ }
+
+ return prototype().hasProperty(arrayIndex);
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has an array index \a arrayIndex,
+ * otherwise returns \c false. The properties of the prototype chain are not
+ * considered.
+ */
+bool QJSManagedValue::hasOwnProperty(quint32 arrayIndex) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (QV4::String *string = d->as<QV4::String>())
+ return arrayIndex < quint32(string->d()->length());
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ if (arrayIndex == std::numeric_limits<quint32>::max()) {
+ return obj->getOwnProperty(obj->engine()->id_uintMax()->toPropertyKey())
+ != QV4::Attr_Invalid;
+ } else {
+ return obj->getOwnProperty(QV4::PropertyKey::fromArrayIndex(arrayIndex))
+ != QV4::Attr_Invalid;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ * Returns the property stored at \a arrayIndex of this QJSManagedValue. The
+ * prototype chain is searched if the property is not found on the actual
+ * object.
+ */
+QJSValue QJSManagedValue::property(quint32 arrayIndex) const
+{
+ if (!d || d->isNullOrUndefined())
+ return QJSValue();
+
+ if (QV4::String *string = d->as<QV4::String>()) {
+ const QString qString = string->toQString();
+ if (arrayIndex < quint32(qString.size()))
+ return qString.sliced(arrayIndex, 1);
+ return QJSValue();
+ }
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ if (arrayIndex == std::numeric_limits<quint32>::max())
+ return QJSValuePrivate::fromReturnedValue(obj->get(obj->engine()->id_uintMax()));
+ else
+ return QJSValuePrivate::fromReturnedValue(obj->get(arrayIndex));
+ }
+
+ return prototype().property(arrayIndex);
+}
+
+/*!
+ * Stores the \a value at \a arrayIndex in this QJSManagedValue. This can only
+ * be done on JavaScript values of type \c object, and it's not recommended if the
+ * value is not an array. Furhermore, \a value has to be either a primitive or
+ * belong to the same engine as this value.
+ */
+void QJSManagedValue::setProperty(quint32 arrayIndex, const QJSValue &value)
+{
+ if (!d)
+ return;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
+ if (Q_UNLIKELY(v4 && v4 != obj->engine())) {
+ qWarning("QJSManagedValue::setProperty() failed: "
+ "Value was created in different engine.");
+ return;
+ }
+ obj->put(arrayIndex, QJSValuePrivate::convertToReturnedValue(v4, value));
+ }
+}
+
+/*!
+ * Deletes the value stored at \a arrayIndex from this QJSManagedValue. Returns
+ * \c true if the deletion succeeded, or \c false otherwise.
+ */
+bool QJSManagedValue::deleteProperty(quint32 arrayIndex)
+{
+ if (!d)
+ return false;
+
+ if (QV4::Object *obj = d->as<QV4::Object>())
+ return obj->deleteProperty(QV4::PropertyKey::fromArrayIndex(arrayIndex));
+
+ return false;
+}
+
+static const QV4::FunctionObject *functionObjectForCall(QV4::Value *d)
+{
+ if (Q_UNLIKELY(!d)) {
+ qWarning("QJSManagedValue: Calling a default-constructed or moved-from managed value"
+ "should throw an exception, but there is no engine to receive it.");
+ return nullptr;
+ }
+
+ if (const QV4::FunctionObject *f = d->as<QV4::FunctionObject>())
+ return f;
+
+ v4Engine(d)->throwTypeError(QStringLiteral("Value is not a function"));
+ return nullptr;
+}
+
+/*!
+ * If this QJSManagedValue represents a JavaScript FunctionObject, calls it with
+ * the given \a arguments, and returns the result. Otherwise returns a
+ * JavaScript \c undefined value.
+ *
+ * The \a arguments have to be either primitive values or belong to the same
+ * QJSEngine as this QJSManagedValue. Otherwise the call is not carried
+ * out and a JavaScript \c undefined value is returned.
+ */
+QJSValue QJSManagedValue::call(const QJSValueList &arguments) const
+{
+ const QV4::FunctionObject *f = functionObjectForCall(d);
+ if (!f)
+ return QJSValue();
+
+ QV4::ExecutionEngine *engine = f->engine();
+
+ QV4::Scope scope(engine);
+ QV4::JSCallArguments jsCallData(scope, arguments.size());
+ *jsCallData.thisObject = engine->globalObject;
+ int i = 0;
+ for (const QJSValue &arg : arguments) {
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::call() failed: Argument was created in different engine.");
+ return QJSValue();
+ }
+ jsCallData.args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ }
+
+ return QJSValuePrivate::fromReturnedValue(f->call(jsCallData));
+}
+
+/*!
+ * If this QJSManagedValue represents a JavaScript FunctionObject, calls it on
+ * \a instance with the given \a arguments, and returns the result. Otherwise
+ * returns a JavaScript \c undefined value.
+ *
+ * The \a arguments and the \a instance have to be either primitive values or
+ * belong to the same QJSEngine as this QJSManagedValue. Otherwise the call is
+ * not carried out and a JavaScript \c undefined value is returned.
+ */
+QJSValue QJSManagedValue::callWithInstance(const QJSValue &instance,
+ const QJSValueList &arguments) const
+{
+ const QV4::FunctionObject *f = functionObjectForCall(d);
+ if (!f)
+ return QJSValue();
+
+ QV4::ExecutionEngine *engine = f->engine();
+
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, instance))) {
+ qWarning("QJSManagedValue::callWithInstance() failed: "
+ "Instance was created in different engine.");
+ return QJSValue();
+ }
+
+ QV4::Scope scope(engine);
+ QV4::JSCallArguments jsCallData(scope, arguments.size());
+ *jsCallData.thisObject = QJSValuePrivate::convertToReturnedValue(engine, instance);
+ int i = 0;
+ for (const QJSValue &arg : arguments) {
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::callWithInstance() failed: "
+ "Argument was created in different engine.");
+ return QJSValue();
+ }
+ jsCallData.args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ }
+
+ return QJSValuePrivate::fromReturnedValue(f->call(jsCallData));
+}
+
+/*!
+ * If this QJSManagedValue represents a JavaScript FunctionObject, calls it as
+ * constructor with the given \a arguments, and returns the result. Otherwise
+ * returns a JavaScript \c undefined value.
+ *
+ * The \a arguments have to be either primitive values or belong to the same
+ * QJSEngine as this QJSManagedValue. Otherwise the call is not carried
+ * out and a JavaScript \c undefined value is returned.
+ */
+QJSValue QJSManagedValue::callAsConstructor(const QJSValueList &arguments) const
+{
+ const QV4::FunctionObject *f = functionObjectForCall(d);
+ if (!f)
+ return QJSValue();
+
+ QV4::ExecutionEngine *engine = f->engine();
+
+ QV4::Scope scope(engine);
+ QV4::JSCallArguments jsCallData(scope, arguments.size());
+ int i = 0;
+ for (const QJSValue &arg : arguments) {
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::callAsConstructor() failed: "
+ "Argument was created in different engine.");
+ return QJSValue();
+ }
+ jsCallData.args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ }
+
+ return QJSValuePrivate::fromReturnedValue(f->callAsConstructor(jsCallData));
+}
+
+/*!
+ * \internal
+ *
+ * Retrieves the JavaScript meta type of this value. The JavaScript meta type
+ * represents the layout of members in an object. Instantiating a meta type is
+ * faster than re-constructing the same object using a sequence of setProperty()
+ * calls on a new object.
+ *
+ * \sa members(), instantiate()
+ */
+QJSManagedValue QJSManagedValue::jsMetaType() const
+{
+ if (!d)
+ return QJSManagedValue();
+
+ QJSManagedValue result(v4Engine(d));
+ if (QV4::Managed *m = d->as<QV4::Managed>())
+ *result.d = m->internalClass();
+
+ return result;
+}
+
+/*!
+ * \internal
+ *
+ * If this value is a JavaScript meta type, retrieves the names of its members
+ * The ordering of the names corresponds to the ordering of the values to be
+ * passed to instantiate().
+ *
+ * If the value is not a meta type, an empty list is returned.
+ *
+ * \sa isMetaType(), metaType(), instantiate()
+ */
+QStringList QJSManagedValue::jsMetaMembers() const
+{
+ if (!d)
+ return {};
+
+ if (QV4::InternalClass *c = d->as<QV4::InternalClass>()) {
+ const auto heapClass = c->d();
+ const int size = heapClass->size;
+ QStringList result;
+ result.reserve(size);
+ QV4::Scope scope(c->engine());
+ for (int i = 0; i < size; ++i) {
+ QV4::ScopedValue key(scope, heapClass->keyAt(i));
+ result.append(key->toQString());
+ }
+ return result;
+ }
+
+ return {};
+}
+
+/*!
+ * \internal
+ *
+ * If this value is a JavaScript meta type, instantiates it using the
+ * \a values, and returns the result. Otherwise returns undefined.
+ *
+ * The values are expected in the same order as the keys in the return value of
+ * members(), and that is the order in which properties were added to the object
+ * this meta type originally belongs to.
+ *
+ * \sa members(), metaType(), isMetaType().
+ */
+QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) const
+{
+ if (!d)
+ return {};
+
+ if (QV4::InternalClass *c = d->as<QV4::InternalClass>()) {
+ QV4::ExecutionEngine *engine = c->engine();
+ QJSManagedValue result(engine);
+ *result.d = c->engine()->newObject(c->d());
+ QV4::Object *o = result.d->as<QV4::Object>();
+
+ for (uint i = 0, end = qMin(qsizetype(c->d()->size), values.size()); i < end; ++i) {
+ const QJSValue &arg = values[i];
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::instantiate() failed: "
+ "Argument was created in different engine.");
+ return QJSManagedValue();
+ }
+ o->setProperty(i, QJSValuePrivate::convertToReturnedValue(engine, arg));
+ }
+
+ return result;
+ }
+
+ return {};
+}
+
+QJSManagedValue::QJSManagedValue(QV4::ExecutionEngine *engine) :
+ d(engine->memoryManager->m_persistentValues->allocate())
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/jsapi/qjsmanagedvalue.h b/src/qml/jsapi/qjsmanagedvalue.h
new file mode 100644
index 0000000000..a6f67b0cc0
--- /dev/null
+++ b/src/qml/jsapi/qjsmanagedvalue.h
@@ -0,0 +1,130 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJSMANAGEDVALUE_H
+#define QJSMANAGEDVALUE_H
+
+#include <QtQml/qtqmlglobal.h>
+#include <QtQml/qjsprimitivevalue.h>
+#include <QtQml/qjsvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+struct Value;
+struct ExecutionEngine;
+}
+
+class QJSEngine;
+class Q_QML_EXPORT QJSManagedValue
+{
+ Q_DISABLE_COPY(QJSManagedValue)
+public:
+ enum Type {
+ Undefined,
+ Boolean,
+ Number,
+ String,
+ Object,
+ Symbol,
+ Function
+ };
+
+ QJSManagedValue() = default;
+ QJSManagedValue(QJSValue value, QJSEngine *engine);
+ QJSManagedValue(const QJSPrimitiveValue &value, QJSEngine *engine);
+ QJSManagedValue(const QVariant &variant, QJSEngine *engine);
+ QJSManagedValue(const QString &string, QJSEngine *engine);
+
+ ~QJSManagedValue();
+ QJSManagedValue(QJSManagedValue &&other);
+ QJSManagedValue &operator=(QJSManagedValue &&other);
+
+ bool equals(const QJSManagedValue &other) const;
+ bool strictlyEquals(const QJSManagedValue &other) const;
+
+ QJSEngine *engine() const;
+
+ QJSManagedValue prototype() const;
+ void setPrototype(const QJSManagedValue &prototype);
+
+ Type type() const;
+
+ // Compatibility with QJSValue
+ bool isUndefined() const { return type() == Undefined; }
+ bool isBoolean() const { return type() == Boolean; }
+ bool isNumber() const { return type() == Number; }
+ bool isString() const { return type() == String; }
+ bool isObject() const { return type() == Object; }
+ bool isSymbol() const { return type() == Symbol; }
+ bool isFunction() const { return type() == Function; }
+
+ // Special case of Number
+ bool isInteger() const;
+
+ // Selected special cases of Object
+ bool isNull() const;
+ bool isRegularExpression() const;
+ bool isArray() const;
+ bool isUrl() const;
+ bool isVariant() const;
+ bool isQObject() const;
+ bool isQMetaObject() const;
+ bool isDate() const;
+ bool isError() const;
+ bool isJsMetaType() const;
+
+ // Native type transformations
+ QString toString() const;
+ double toNumber() const;
+ bool toBoolean() const;
+
+ // Variant-like type transformations
+ QJSPrimitiveValue toPrimitive() const;
+ QJSValue toJSValue() const;
+ QVariant toVariant() const;
+
+ // Special cases
+ int toInteger() const;
+ QRegularExpression toRegularExpression() const;
+ QUrl toUrl() const;
+ QObject *toQObject() const;
+ const QMetaObject *toQMetaObject() const;
+ QDateTime toDateTime() const;
+
+ // Properties of objects
+ bool hasProperty(const QString &name) const;
+ bool hasOwnProperty(const QString &name) const;
+ QJSValue property(const QString &name) const;
+ void setProperty(const QString &name, const QJSValue &value);
+ bool deleteProperty(const QString &name);
+
+ // ### Qt 7 use qsizetype instead.
+ // Array indexing
+ bool hasProperty(quint32 arrayIndex) const;
+ bool hasOwnProperty(quint32 arrayIndex) const;
+ QJSValue property(quint32 arrayIndex) const;
+ void setProperty(quint32 arrayIndex, const QJSValue &value);
+ bool deleteProperty(quint32 arrayIndex);
+
+ // Calling functions
+ QJSValue call(const QJSValueList &arguments = {}) const;
+ QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &arguments = {}) const;
+ QJSValue callAsConstructor(const QJSValueList &arguments = {}) const;
+
+ // JavaScript metatypes
+ QJSManagedValue jsMetaType() const;
+ QStringList jsMetaMembers() const;
+ QJSManagedValue jsMetaInstantiate(const QJSValueList &values = {}) const;
+
+private:
+ friend class QJSValue;
+ friend class QJSEngine;
+
+ QJSManagedValue(QV4::ExecutionEngine *engine);
+ QV4::Value *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsapi/qjsprimitivevalue.cpp b/src/qml/jsapi/qjsprimitivevalue.cpp
new file mode 100644
index 0000000000..4bd418e082
--- /dev/null
+++ b/src/qml/jsapi/qjsprimitivevalue.cpp
@@ -0,0 +1,340 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qjsprimitivevalue.h"
+
+#include <QtQml/private/qv4runtime_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \since 6.1
+ \class QJSPrimitiveUndefined
+
+ \inmodule QtQml
+
+ \brief An empty marker type to signify the JavaScript Undefined type and its single value.
+ \inmodule QtQml
+ */
+
+/*!
+ \since 6.1
+ \class QJSPrimitiveNull
+
+ \inmodule QtQml
+
+ \brief An empty marker type to signify the JavaScript null value.
+ \inmodule QtQml
+ */
+
+/*!
+ \since 6.1
+ \class QJSPrimitiveValue
+
+ \brief The QJSPrimitiveValue class operates on primitive types in JavaScript semantics.
+
+ \ingroup qtjavascript
+ \inmodule QtQml
+
+ QJSPrimitiveValue supports most of the primitive types defined in the
+ \l{ECMA-262} standard, in particular Undefined, Boolean, Number, and String.
+ Additionally, you can store a JavaScript null in a QJSPrimitiveValue and as a
+ special case of Number, you can store an integer value.
+
+ All those values are stored immediately, without interacting with the
+ JavaScript heap. Therefore, you can pass QJSPrimitiveValues between different
+ JavaScript engines. In contrast to QJSManagedValue, there is also no danger
+ in destroying a QJSPrimitiveValue from a different thread than it was created
+ in. On the flip side, QJSPrimitiveValue does not hold a reference to any
+ JavaScript engine.
+
+ QJSPrimitiveValue implements the JavaScript arithmetic and comparison
+ operators on the supported types in JavaScript semantics. Types are coerced
+ like the JavaScript engine would coerce them if the operators were written
+ in a JavaScript expression.
+
+ The JavaScript Symbol type is not supported as it is of very limited utility
+ regarding arithmetic and comparison operators, the main purpose of
+ QJSPrimitiveValue. In particular, it causes an exception whenever you try to
+ coerce it to a number or a string, and we cannot throw exceptions without a
+ JavaScript Engine.
+ */
+
+/*!
+ \enum QJSPrimitiveValue::Type
+
+ This enum speicifies the types a QJSPrimitiveValue might contain.
+
+ \value Undefined The JavaScript Undefined value.
+ \value Null The JavaScript null value. This is in fact not a separate
+ JavaScript type but a special value of the Object type. As it is
+ very common and storable without JavaScript engine, it is still
+ supported.
+ \value Boolean A JavaScript Boolean value.
+ \value Integer An integer. This is a special case of the JavaScript Number
+ type. JavaScript does not have an actual integer type, but
+ the \l{ECMA-262} standard contains rules on how to transform a
+ Number in order to prepare it for certain operators that only
+ make sense on integers, in particular the bit shift operators.
+ QJSPrimitiveValue's Integer type represents the result of such
+ a transformation.
+ \value Double A JavaScript Number value.
+ \value String A JavaScript String value.
+ */
+
+/*!
+ \fn Type QJSPrimitiveValue::type() const
+
+ Returns the type of the QJSPrimitiveValue.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue()
+
+ Creates a QJSPrimitiveValue of type Undefined.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(QJSPrimitiveUndefined undefined)
+
+ Creates a QJSPrimitiveValue of value \a undefined and type Undefined.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(QJSPrimitiveNull null)
+
+ Creates a QJSPrimitiveValue of value \a null and type Null.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(bool value)
+
+ Creates a QJSPrimitiveValue of value \a value and type Boolean.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(int value)
+
+ Creates a QJSPrimitiveValue of value \a value and type Integer.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(double value)
+
+ Creates a QJSPrimitiveValue of value \a value and type Double.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(QString value)
+
+ Creates a QJSPrimitiveValue of value \a value and type String.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(QMetaType type, const void *value)
+ \since 6.4
+
+ Creates a QJSPrimitiveValue of type \a type, and initializes with
+ \a value if \a type can be stored in QJSPrimtiveValue. \a value must not
+ be nullptr in that case. If \a type cannot be stored this results in a
+ QJSPrimitiveValue of type Undefined.
+
+ Note that you have to pass the address of the variable you want stored.
+
+ Usually, you never have to use this constructor, use the one taking QVariant
+ instead.
+ */
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(QMetaType type)
+ \since 6.6
+ \internal
+
+ Creates a QJSPrimitiveValue of type \a type, and initializes with a
+ default-constructed value if \a type can be stored in QJSPrimtiveValue.
+ If \a type cannot be stored this results in a QJSPrimitiveValue of type
+ Undefined.
+*/
+
+/*!
+ \fn QJSPrimitiveValue::QJSPrimitiveValue(const QVariant &value)
+
+ Creates a QJSPrimitiveValue from the contents of \a value if those contents
+ can be stored in QJSPrimtiveValue. Otherwise this results in a
+ QJSPrimitiveValue of type Undefined.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::toBoolean() const
+
+ Returns the value coerced a boolean by JavaScript rules.
+ */
+
+/*!
+ \fn int QJSPrimitiveValue::toInteger() const
+
+ Returns the value coerced to an integral 32bit number by the rules JavaScript
+ would apply when preparing it for a bit shift operation.
+ */
+
+/*!
+ \fn double QJSPrimitiveValue::toDouble() const
+
+ Returns the value coerced to a JavaScript Number by JavaScript rules.
+ */
+
+/*!
+ \fn QString QJSPrimitiveValue::toString() const
+
+ Returns the value coerced to a JavaScript String by JavaScript rules.
+ */
+
+/*!
+ \fn QJSPrimitiveValue QJSPrimitiveValue::operator+(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+
+ \since 6.1
+
+ Perfoms the JavaScript '+' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn QJSPrimitiveValue QJSPrimitiveValue::operator-(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '-' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn QJSPrimitiveValue QJSPrimitiveValue::operator*(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '*' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn QJSPrimitiveValue QJSPrimitiveValue::operator/(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '/' operation between \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::strictlyEquals(const QJSPrimitiveValue &other) const
+
+ Performs the JavaScript '===' operation on this QJSPrimitiveValue and
+ \a other, and returns the result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::equals(const QJSPrimitiveValue &other) const
+
+ Performs the JavaScript '==' operation on this QJSPrimitiveValue and
+ \a other, and returns the result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::operator==(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '===' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::operator!=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '!==' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::operator<(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '<' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '>' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '<=' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn bool QJSPrimitiveValue::operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ \since 6.1
+
+ Performs the JavaScript '>=' operation on \a lhs and \a rhs, and returns the
+ result.
+ */
+
+/*!
+ \fn QMetaType QJSPrimitiveValue::metaType() const
+ \since 6.6
+
+ Returns the QMetaType of the value stored in the QJSPrimitiveValue.
+ */
+
+/*!
+ \fn const void *QJSPrimitiveValue::constData() const
+ \fn const void *QJSPrimitiveValue::data() const
+ \since 6.6
+
+ Returns a pointer to the contained value as a generic void* that cannot be
+ written to.
+ */
+
+/*!
+ \fn const void *QJSPrimitiveValue::data()
+ \since 6.6
+
+ Returns a pointer to the contained data as a generic void* that can be
+ written to.
+*/
+
+/*!
+ \fn template<QJSPrimitiveValue::Type type> QJSPrimitiveValue QJSPrimitiveValue::to() const
+ \since 6.6
+
+ Coerces the value to the specified \e type and returns the result as a new
+ QJSPrimitiveValue.
+
+ \sa toBoolean(), toInteger(), toDouble(), toString()
+*/
+
+QString QJSPrimitiveValue::toString(double d)
+{
+ QString result;
+ QV4::RuntimeHelpers::numberToString(&result, d);
+ return result;
+}
+
+/*!
+ \fn double QQmlPrivate::jsExponentiate(double base, double exponent)
+ \internal
+ \since 6.4
+
+ Performs JavaScript's Number::exponentiate operation on \a base and
+ \a exponent, and returns the result.
+
+ See https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate
+ */
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/jsapi/qjsprimitivevalue.h b/src/qml/jsapi/qjsprimitivevalue.h
new file mode 100644
index 0000000000..4ba3fd7dc3
--- /dev/null
+++ b/src/qml/jsapi/qjsprimitivevalue.h
@@ -0,0 +1,966 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJSPRIMITIVEVALUE_H
+#define QJSPRIMITIVEVALUE_H
+
+#include <QtQml/qtqmlglobal.h>
+#include <QtQml/qjsnumbercoercion.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qvariant.h>
+
+#include <variant>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 { struct ExecutionEngine; }
+
+struct QJSPrimitiveUndefined {};
+struct QJSPrimitiveNull {};
+
+class QJSPrimitiveValue
+{
+ template<typename Concrete>
+ struct StringNaNOperators
+ {
+ static constexpr double op(const QString &, QJSPrimitiveUndefined)
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ static constexpr double op(QJSPrimitiveUndefined, const QString &)
+ {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ static double op(const QString &lhs, QJSPrimitiveNull) { return op(lhs, 0); }
+ static double op(QJSPrimitiveNull, const QString &rhs) { return op(0, rhs); }
+
+ template<typename T>
+ static double op(const QString &lhs, T rhs)
+ {
+ return Concrete::op(fromString(lhs).toDouble(), rhs);
+ }
+
+ template<typename T>
+ static double op(T lhs, const QString &rhs)
+ {
+ return Concrete::op(lhs, fromString(rhs).toDouble());
+ }
+
+ static double op(const QString &lhs, const QString &rhs)
+ {
+ return Concrete::op(fromString(lhs).toDouble(), fromString(rhs).toDouble());
+ }
+ };
+
+ struct AddOperators {
+ static constexpr double op(double lhs, double rhs) { return lhs + rhs; }
+ static bool opOverflow(int lhs, int rhs, int *result)
+ {
+ return qAddOverflow(lhs, rhs, result);
+ }
+
+ template<typename T>
+ static QString op(const QString &lhs, T rhs)
+ {
+ return lhs + QJSPrimitiveValue(rhs).toString();
+ }
+
+ template<typename T>
+ static QString op(T lhs, const QString &rhs)
+ {
+ return QJSPrimitiveValue(lhs).toString() + rhs;
+ }
+
+ static QString op(const QString &lhs, const QString &rhs) { return lhs + rhs; }
+ };
+
+ struct SubOperators : private StringNaNOperators<SubOperators> {
+ static constexpr double op(double lhs, double rhs) { return lhs - rhs; }
+ static bool opOverflow(int lhs, int rhs, int *result)
+ {
+ return qSubOverflow(lhs, rhs, result);
+ }
+
+ using StringNaNOperators::op;
+ };
+
+ struct MulOperators : private StringNaNOperators<MulOperators> {
+ static constexpr double op(double lhs, double rhs) { return lhs * rhs; }
+ static bool opOverflow(int lhs, int rhs, int *result)
+ {
+ // compare mul_int32 in qv4math_p.h
+ auto hadOverflow = qMulOverflow(lhs, rhs, result);
+ if (((lhs < 0) ^ (rhs < 0)) && (*result == 0))
+ return true; // result must be negative 0, does not fit into int
+ return hadOverflow;
+ }
+
+ using StringNaNOperators::op;
+ };
+
+ struct DivOperators : private StringNaNOperators<DivOperators> {
+ static constexpr double op(double lhs, double rhs) { return lhs / rhs; }
+ static constexpr bool opOverflow(int, int, int *)
+ {
+ return true;
+ }
+
+ using StringNaNOperators::op;
+ };
+
+public:
+ enum Type : quint8 {
+ Undefined,
+ Null,
+ Boolean,
+ Integer,
+ Double,
+ String
+ };
+
+ constexpr Type type() const { return Type(d.type()); }
+
+ // Prevent casting from Type to int
+ QJSPrimitiveValue(Type) = delete;
+
+ Q_IMPLICIT constexpr QJSPrimitiveValue() noexcept = default;
+ Q_IMPLICIT constexpr QJSPrimitiveValue(QJSPrimitiveUndefined undefined) noexcept : d(undefined) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(QJSPrimitiveNull null) noexcept : d(null) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(bool value) noexcept : d(value) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(int value) noexcept : d(value) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(double value) noexcept : d(value) {}
+ Q_IMPLICIT QJSPrimitiveValue(QString string) noexcept : d(std::move(string)) {}
+
+ explicit QJSPrimitiveValue(const QMetaType type, const void *value) noexcept
+ {
+ switch (type.id()) {
+ case QMetaType::UnknownType:
+ d = QJSPrimitiveUndefined();
+ break;
+ case QMetaType::Nullptr:
+ d = QJSPrimitiveNull();
+ break;
+ case QMetaType::Bool:
+ d = *static_cast<const bool *>(value);
+ break;
+ case QMetaType::Int:
+ d = *static_cast<const int *>(value);
+ break;
+ case QMetaType::Double:
+ d = *static_cast<const double *>(value);
+ break;
+ case QMetaType::QString:
+ d = *static_cast<const QString *>(value);
+ break;
+ default:
+ // Unsupported. Remains undefined.
+ break;
+ }
+ }
+
+ explicit QJSPrimitiveValue(QMetaType type) noexcept
+ {
+ switch (type.id()) {
+ case QMetaType::UnknownType:
+ d = QJSPrimitiveUndefined();
+ break;
+ case QMetaType::Nullptr:
+ d = QJSPrimitiveNull();
+ break;
+ case QMetaType::Bool:
+ d = false;
+ break;
+ case QMetaType::Int:
+ d = 0;
+ break;
+ case QMetaType::Double:
+ d = 0.0;
+ break;
+ case QMetaType::QString:
+ d = QString();
+ break;
+ default:
+ // Unsupported. Remains undefined.
+ break;
+ }
+ }
+
+ explicit QJSPrimitiveValue(const QVariant &variant) noexcept
+ : QJSPrimitiveValue(variant.metaType(), variant.data())
+ {
+ }
+
+ constexpr QMetaType metaType() const { return d.metaType(); }
+ constexpr void *data() { return d.data(); }
+ constexpr const void *data() const { return d.data(); }
+ constexpr const void *constData() const { return d.data(); }
+
+ template<Type type>
+ QJSPrimitiveValue to() const {
+ if constexpr (type == Undefined)
+ return QJSPrimitiveUndefined();
+ if constexpr (type == Null)
+ return QJSPrimitiveNull();
+ if constexpr (type == Boolean)
+ return toBoolean();
+ if constexpr (type == Integer)
+ return toInteger();
+ if constexpr (type == Double)
+ return toDouble();
+ if constexpr (type == String)
+ return toString();
+
+ Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
+ }
+
+ constexpr bool toBoolean() const
+ {
+ switch (type()) {
+ case Undefined: return false;
+ case Null: return false;
+ case Boolean: return asBoolean();
+ case Integer: return asInteger() != 0;
+ case Double: {
+ const double v = asDouble();
+ return !QJSNumberCoercion::equals(v, 0) && !std::isnan(v);
+ }
+ case String: return !asString().isEmpty();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
+ return false;
+ #endif
+ }
+
+ constexpr int toInteger() const
+ {
+ switch (type()) {
+ case Undefined: return 0;
+ case Null: return 0;
+ case Boolean: return asBoolean();
+ case Integer: return asInteger();
+ case Double: return QJSNumberCoercion::toInteger(asDouble());
+ case String: return fromString(asString()).toInteger();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(0);
+ #else
+ return 0;
+ #endif
+ }
+
+ constexpr double toDouble() const
+ {
+ switch (type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return 0;
+ case Boolean: return asBoolean();
+ case Integer: return asInteger();
+ case Double: return asDouble();
+ case String: return fromString(asString()).toDouble();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN({});
+ #else
+ return {};
+ #endif
+ }
+
+ QString toString() const
+ {
+ switch (type()) {
+ case Undefined: return QStringLiteral("undefined");
+ case Null: return QStringLiteral("null");
+ case Boolean: return asBoolean() ? QStringLiteral("true") : QStringLiteral("false");
+ case Integer: return QString::number(asInteger());
+ case Double: {
+ const double result = asDouble();
+ if (std::isnan(result))
+ return QStringLiteral("NaN");
+ if (std::isfinite(result))
+ return toString(result);
+ if (result > 0)
+ return QStringLiteral("Infinity");
+ return QStringLiteral("-Infinity");
+ }
+ case String: return asString();
+ }
+
+ Q_UNREACHABLE_RETURN(QString());
+ }
+
+ QVariant toVariant() const
+ {
+ switch (type()) {
+ case Undefined: return QVariant();
+ case Null: return QVariant::fromValue<std::nullptr_t>(nullptr);
+ case Boolean: return QVariant(asBoolean());
+ case Integer: return QVariant(asInteger());
+ case Double: return QVariant(asDouble());
+ case String: return QVariant(asString());
+ }
+
+ Q_UNREACHABLE_RETURN(QVariant());
+ }
+
+ friend inline QJSPrimitiveValue operator+(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ return operate<AddOperators>(lhs, rhs);
+ }
+
+ friend inline QJSPrimitiveValue operator-(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ return operate<SubOperators>(lhs, rhs);
+ }
+
+ friend inline QJSPrimitiveValue operator*(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ return operate<MulOperators>(lhs, rhs);
+ }
+
+ friend inline QJSPrimitiveValue operator/(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ return operate<DivOperators>(lhs, rhs);
+ }
+
+ friend inline QJSPrimitiveValue operator%(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ switch (lhs.type()) {
+ case Null:
+ case Boolean:
+ case Integer:
+ switch (rhs.type()) {
+ case Boolean:
+ case Integer: {
+ const int leftInt = lhs.toInteger();
+ const int rightInt = rhs.toInteger();
+ if (leftInt >= 0 && rightInt > 0)
+ return leftInt % rightInt;
+ Q_FALLTHROUGH();
+ }
+ default:
+ break;
+ }
+ Q_FALLTHROUGH();
+ default:
+ break;
+ }
+
+ return std::fmod(lhs.toDouble(), rhs.toDouble());
+ }
+
+ QJSPrimitiveValue &operator++()
+ {
+ // ++a is modeled as a -= (-1) to avoid the potential string concatenation
+ return (*this = operate<SubOperators>(*this, -1));
+ }
+
+ QJSPrimitiveValue operator++(int)
+ {
+ // a++ is modeled as a -= (-1) to avoid the potential string concatenation
+ QJSPrimitiveValue other = operate<SubOperators>(*this, -1);
+ std::swap(other, *this);
+ return +other; // We still need to coerce the original value.
+ }
+
+ QJSPrimitiveValue &operator--()
+ {
+ return (*this = operate<SubOperators>(*this, 1));
+ }
+
+ QJSPrimitiveValue operator--(int)
+ {
+ QJSPrimitiveValue other = operate<SubOperators>(*this, 1);
+ std::swap(other, *this);
+ return +other; // We still need to coerce the original value.
+ }
+
+ QJSPrimitiveValue operator+()
+ {
+ // +a is modeled as a -= 0. That should force it to number.
+ return (*this = operate<SubOperators>(*this, 0));
+ }
+
+ QJSPrimitiveValue operator-()
+ {
+ return (*this = operate<MulOperators>(*this, -1));
+ }
+
+ constexpr bool strictlyEquals(const QJSPrimitiveValue &other) const
+ {
+ const Type myType = type();
+ const Type otherType = other.type();
+
+ if (myType != otherType) {
+ // int -> double promotion is OK in strict mode
+ if (myType == Double && otherType == Integer)
+ return strictlyEquals(double(other.asInteger()));
+ if (myType == Integer && otherType == Double)
+ return QJSPrimitiveValue(double(asInteger())).strictlyEquals(other);
+ return false;
+ }
+
+ switch (myType) {
+ case Undefined:
+ case Null:
+ return true;
+ case Boolean:
+ return asBoolean() == other.asBoolean();
+ case Integer:
+ return asInteger() == other.asInteger();
+ case Double: {
+ const double l = asDouble();
+ const double r = other.asDouble();
+ if (std::isnan(l) || std::isnan(r))
+ return false;
+ if (qIsNull(l) && qIsNull(r))
+ return true;
+ return QJSNumberCoercion::equals(l, r);
+ }
+ case String:
+ return asString() == other.asString();
+ }
+
+ return false;
+ }
+
+ // Loose operator==, in contrast to strict ===
+ constexpr bool equals(const QJSPrimitiveValue &other) const
+ {
+ const Type myType = type();
+ const Type otherType = other.type();
+
+ if (myType == otherType)
+ return strictlyEquals(other);
+
+ switch (myType) {
+ case Undefined:
+ return otherType == Null;
+ case Null:
+ return otherType == Undefined;
+ case Boolean:
+ return QJSPrimitiveValue(int(asBoolean())).equals(other);
+ case Integer:
+ // prefer rhs bool -> int promotion over promoting both to double
+ return otherType == Boolean
+ ? QJSPrimitiveValue(asInteger()).equals(int(other.asBoolean()))
+ : QJSPrimitiveValue(double(asInteger())).equals(other);
+ case Double:
+ // Promote the other side to double (or recognize lhs as undefined/null)
+ return other.equals(*this);
+ case String:
+ return fromString(asString()).parsedEquals(other);
+ }
+
+ return false;
+ }
+
+ friend constexpr inline bool operator==(const QJSPrimitiveValue &lhs, const
+ QJSPrimitiveValue &rhs)
+ {
+ return lhs.strictlyEquals(rhs);
+ }
+
+ friend constexpr inline bool operator!=(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ return !lhs.strictlyEquals(rhs);
+ }
+
+ friend constexpr inline bool operator<(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ switch (lhs.type()) {
+ case Undefined:
+ return false;
+ case Null: {
+ switch (rhs.type()) {
+ case Undefined: return false;
+ case Null: return false;
+ case Boolean: return 0 < int(rhs.asBoolean());
+ case Integer: return 0 < rhs.asInteger();
+ case Double: return double(0) < rhs.asDouble();
+ case String: return double(0) < rhs.toDouble();
+ }
+ break;
+ }
+ case Boolean: {
+ switch (rhs.type()) {
+ case Undefined: return false;
+ case Null: return int(lhs.asBoolean()) < 0;
+ case Boolean: return lhs.asBoolean() < rhs.asBoolean();
+ case Integer: return int(lhs.asBoolean()) < rhs.asInteger();
+ case Double: return double(lhs.asBoolean()) < rhs.asDouble();
+ case String: return double(lhs.asBoolean()) < rhs.toDouble();
+ }
+ break;
+ }
+ case Integer: {
+ switch (rhs.type()) {
+ case Undefined: return false;
+ case Null: return lhs.asInteger() < 0;
+ case Boolean: return lhs.asInteger() < int(rhs.asBoolean());
+ case Integer: return lhs.asInteger() < rhs.asInteger();
+ case Double: return double(lhs.asInteger()) < rhs.asDouble();
+ case String: return double(lhs.asInteger()) < rhs.toDouble();
+ }
+ break;
+ }
+ case Double: {
+ switch (rhs.type()) {
+ case Undefined: return false;
+ case Null: return lhs.asDouble() < double(0);
+ case Boolean: return lhs.asDouble() < double(rhs.asBoolean());
+ case Integer: return lhs.asDouble() < double(rhs.asInteger());
+ case Double: return lhs.asDouble() < rhs.asDouble();
+ case String: return lhs.asDouble() < rhs.toDouble();
+ }
+ break;
+ }
+ case String: {
+ switch (rhs.type()) {
+ case Undefined: return false;
+ case Null: return lhs.toDouble() < double(0);
+ case Boolean: return lhs.toDouble() < double(rhs.asBoolean());
+ case Integer: return lhs.toDouble() < double(rhs.asInteger());
+ case Double: return lhs.toDouble() < rhs.asDouble();
+ case String: return lhs.asString() < rhs.asString();
+ }
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ friend constexpr inline bool operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ {
+ return rhs < lhs;
+ }
+
+ friend constexpr inline bool operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ {
+ if (lhs.type() == String) {
+ if (rhs.type() == String)
+ return lhs.asString() <= rhs.asString();
+ else
+ return fromString(lhs.asString()) <= rhs;
+ }
+ if (rhs.type() == String)
+ return lhs <= fromString(rhs.asString());
+
+ if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined())
+ return false;
+ return !(lhs > rhs);
+ }
+
+ friend constexpr inline bool operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ {
+ if (lhs.type() == String) {
+ if (rhs.type() == String)
+ return lhs.asString() >= rhs.asString();
+ else
+ return fromString(lhs.asString()) >= rhs;
+ }
+ if (rhs.type() == String)
+ return lhs >= fromString(rhs.asString());
+
+ if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined())
+ return false;
+ return !(lhs < rhs);
+ }
+
+private:
+ friend class QJSManagedValue;
+ friend class QJSValue;
+ friend struct QV4::ExecutionEngine;
+
+ constexpr bool asBoolean() const { return d.getBool(); }
+ constexpr int asInteger() const { return d.getInt(); }
+ constexpr double asDouble() const { return d.getDouble(); }
+ QString asString() const { return d.getString(); }
+
+ constexpr bool parsedEquals(const QJSPrimitiveValue &other) const
+ {
+ return type() != Undefined && equals(other);
+ }
+
+ static QJSPrimitiveValue fromString(const QString &string)
+ {
+ bool ok;
+ const int intValue = string.toInt(&ok);
+ if (ok)
+ return intValue;
+
+ const double doubleValue = string.toDouble(&ok);
+ if (ok)
+ return doubleValue;
+ if (string == QStringLiteral("Infinity"))
+ return std::numeric_limits<double>::infinity();
+ if (string == QStringLiteral("-Infinity"))
+ return -std::numeric_limits<double>::infinity();
+ if (string == QStringLiteral("NaN"))
+ return std::numeric_limits<double>::quiet_NaN();
+ return QJSPrimitiveUndefined();
+ }
+
+ static Q_QML_EXPORT QString toString(double d);
+
+ template<typename Operators, typename Lhs, typename Rhs>
+ static QJSPrimitiveValue operateOnIntegers(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ int result;
+ if (Operators::opOverflow(lhs.d.get<Lhs>(), rhs.d.get<Rhs>(), &result))
+ return Operators::op(lhs.d.get<Lhs>(), rhs.d.get<Rhs>());
+ return result;
+ }
+
+ template<typename Operators>
+ static QJSPrimitiveValue operate(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
+ {
+ switch (lhs.type()) {
+ case Undefined:
+ switch (rhs.type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return std::numeric_limits<double>::quiet_NaN();
+ case Boolean: return std::numeric_limits<double>::quiet_NaN();
+ case Integer: return std::numeric_limits<double>::quiet_NaN();
+ case Double: return std::numeric_limits<double>::quiet_NaN();
+ case String: return Operators::op(QJSPrimitiveUndefined(), rhs.asString());
+ }
+ break;
+ case Null:
+ switch (rhs.type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return operateOnIntegers<Operators, int, int>(0, 0);
+ case Boolean: return operateOnIntegers<Operators, int, bool>(0, rhs);
+ case Integer: return operateOnIntegers<Operators, int, int>(0, rhs);
+ case Double: return Operators::op(0, rhs.asDouble());
+ case String: return Operators::op(QJSPrimitiveNull(), rhs.asString());
+ }
+ break;
+ case Boolean:
+ switch (rhs.type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return operateOnIntegers<Operators, bool, int>(lhs, 0);
+ case Boolean: return operateOnIntegers<Operators, bool, bool>(lhs, rhs);
+ case Integer: return operateOnIntegers<Operators, bool, int>(lhs, rhs);
+ case Double: return Operators::op(lhs.asBoolean(), rhs.asDouble());
+ case String: return Operators::op(lhs.asBoolean(), rhs.asString());
+ }
+ break;
+ case Integer:
+ switch (rhs.type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return operateOnIntegers<Operators, int, int>(lhs, 0);
+ case Boolean: return operateOnIntegers<Operators, int, bool>(lhs, rhs);
+ case Integer: return operateOnIntegers<Operators, int, int>(lhs, rhs);
+ case Double: return Operators::op(lhs.asInteger(), rhs.asDouble());
+ case String: return Operators::op(lhs.asInteger(), rhs.asString());
+ }
+ break;
+ case Double:
+ switch (rhs.type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return Operators::op(lhs.asDouble(), 0);
+ case Boolean: return Operators::op(lhs.asDouble(), rhs.asBoolean());
+ case Integer: return Operators::op(lhs.asDouble(), rhs.asInteger());
+ case Double: return Operators::op(lhs.asDouble(), rhs.asDouble());
+ case String: return Operators::op(lhs.asDouble(), rhs.asString());
+ }
+ break;
+ case String:
+ switch (rhs.type()) {
+ case Undefined: return Operators::op(lhs.asString(), QJSPrimitiveUndefined());
+ case Null: return Operators::op(lhs.asString(), QJSPrimitiveNull());
+ case Boolean: return Operators::op(lhs.asString(), rhs.asBoolean());
+ case Integer: return Operators::op(lhs.asString(), rhs.asInteger());
+ case Double: return Operators::op(lhs.asString(), rhs.asDouble());
+ case String: return Operators::op(lhs.asString(), rhs.asString());
+ }
+ break;
+ }
+
+ Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
+ }
+
+ constexpr bool isNanOrUndefined() const
+ {
+ switch (type()) {
+ case Undefined: return true;
+ case Double: return std::isnan(asDouble());
+ default: return false;
+ }
+ }
+
+ struct QJSPrimitiveValuePrivate
+ {
+ // Can't be default because QString has a non-trivial ctor.
+ constexpr QJSPrimitiveValuePrivate() noexcept {}
+
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveUndefined) noexcept {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveNull) noexcept
+ : m_type(Null) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(bool b) noexcept
+ : m_bool(b), m_type(Boolean) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(int i) noexcept
+ : m_int(i), m_type(Integer) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(double d) noexcept
+ : m_double(d), m_type(Double) {}
+ Q_IMPLICIT QJSPrimitiveValuePrivate(QString s) noexcept
+ : m_string(std::move(s)), m_type(String) {}
+
+ constexpr QJSPrimitiveValuePrivate(const QJSPrimitiveValuePrivate &other) noexcept
+ : m_type(other.m_type)
+ {
+ // Not copy-and-swap since swap() would be much more complicated.
+ if (!assignSimple(other))
+ new (&m_string) QString(other.m_string);
+ }
+
+ constexpr QJSPrimitiveValuePrivate(QJSPrimitiveValuePrivate &&other) noexcept
+ : m_type(other.m_type)
+ {
+ // Not move-and-swap since swap() would be much more complicated.
+ if (!assignSimple(other))
+ new (&m_string) QString(std::move(other.m_string));
+ }
+
+ constexpr QJSPrimitiveValuePrivate &operator=(const QJSPrimitiveValuePrivate &other) noexcept
+ {
+ if (this == &other)
+ return *this;
+
+ if (m_type == String) {
+ if (other.m_type == String) {
+ m_type = other.m_type;
+ m_string = other.m_string;
+ return *this;
+ }
+ m_string.~QString();
+ }
+
+ m_type = other.m_type;
+ if (!assignSimple(other))
+ new (&m_string) QString(other.m_string);
+ return *this;
+ }
+
+ constexpr QJSPrimitiveValuePrivate &operator=(QJSPrimitiveValuePrivate &&other) noexcept
+ {
+ if (this == &other)
+ return *this;
+
+ if (m_type == String) {
+ if (other.m_type == String) {
+ m_type = other.m_type;
+ m_string = std::move(other.m_string);
+ return *this;
+ }
+ m_string.~QString();
+ }
+
+ m_type = other.m_type;
+ if (!assignSimple(other))
+ new (&m_string) QString(std::move(other.m_string));
+ return *this;
+ }
+
+ ~QJSPrimitiveValuePrivate()
+ {
+ if (m_type == String)
+ m_string.~QString();
+ }
+
+ constexpr Type type() const noexcept { return m_type; }
+ constexpr bool getBool() const noexcept { return m_bool; }
+ constexpr int getInt() const noexcept { return m_int; }
+ constexpr double getDouble() const noexcept { return m_double; }
+ QString getString() const noexcept { return m_string; }
+
+ template<typename T>
+ constexpr T get() const noexcept {
+ if constexpr (std::is_same_v<T, QJSPrimitiveUndefined>)
+ return QJSPrimitiveUndefined();
+ else if constexpr (std::is_same_v<T, QJSPrimitiveNull>)
+ return QJSPrimitiveNull();
+ else if constexpr (std::is_same_v<T, bool>)
+ return getBool();
+ else if constexpr (std::is_same_v<T, int>)
+ return getInt();
+ else if constexpr (std::is_same_v<T, double>)
+ return getDouble();
+ else if constexpr (std::is_same_v<T, QString>)
+ return getString();
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(T());
+ #else
+ return T();
+ #endif
+ }
+
+ constexpr QMetaType metaType() const noexcept {
+ switch (m_type) {
+ case Undefined:
+ return QMetaType();
+ case Null:
+ return QMetaType::fromType<std::nullptr_t>();
+ case Boolean:
+ return QMetaType::fromType<bool>();
+ case Integer:
+ return QMetaType::fromType<int>();
+ case Double:
+ return QMetaType::fromType<double>();
+ case String:
+ return QMetaType::fromType<QString>();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(QMetaType());
+ #else
+ return QMetaType();
+ #endif
+ }
+
+ constexpr void *data() noexcept {
+ switch (m_type) {
+ case Undefined:
+ case Null:
+ return nullptr;
+ case Boolean:
+ return &m_bool;
+ case Integer:
+ return &m_int;
+ case Double:
+ return &m_double;
+ case String:
+ return &m_string;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(nullptr);
+ #else
+ return nullptr;
+ #endif
+ }
+
+ constexpr const void *data() const noexcept {
+ switch (m_type) {
+ case Undefined:
+ case Null:
+ return nullptr;
+ case Boolean:
+ return &m_bool;
+ case Integer:
+ return &m_int;
+ case Double:
+ return &m_double;
+ case String:
+ return &m_string;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(nullptr);
+ #else
+ return nullptr;
+ #endif
+ }
+
+ private:
+ constexpr bool assignSimple(const QJSPrimitiveValuePrivate &other) noexcept
+ {
+ switch (other.m_type) {
+ case Undefined:
+ case Null:
+ return true;
+ case Boolean:
+ m_bool = other.m_bool;
+ return true;
+ case Integer:
+ m_int = other.m_int;
+ return true;
+ case Double:
+ m_double = other.m_double;
+ return true;
+ case String:
+ return false;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
+ return false;
+ #endif
+ }
+
+ union {
+ bool m_bool = false;
+ int m_int;
+ double m_double;
+ QString m_string;
+ };
+
+ Type m_type = Undefined;
+ };
+
+ QJSPrimitiveValuePrivate d;
+};
+
+namespace QQmlPrivate {
+ // TODO: Make this constexpr once std::isnan is constexpr.
+ inline double jsExponentiate(double base, double exponent)
+ {
+ constexpr double qNaN = std::numeric_limits<double>::quiet_NaN();
+ constexpr double inf = std::numeric_limits<double>::infinity();
+
+ if (qIsNull(exponent))
+ return 1.0;
+
+ if (std::isnan(exponent))
+ return qNaN;
+
+ if (QJSNumberCoercion::equals(base, 1.0) || QJSNumberCoercion::equals(base, -1.0))
+ return std::isinf(exponent) ? qNaN : std::pow(base, exponent);
+
+ if (!qIsNull(base))
+ return std::pow(base, exponent);
+
+ if (std::copysign(1.0, base) > 0.0)
+ return exponent < 0.0 ? inf : std::pow(base, exponent);
+
+ if (exponent < 0.0)
+ return QJSNumberCoercion::equals(std::fmod(-exponent, 2.0), 1.0) ? -inf : inf;
+
+ return QJSNumberCoercion::equals(std::fmod(exponent, 2.0), 1.0)
+ ? std::copysign(0, -1.0)
+ : 0.0;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QJSPRIMITIVEVALUE_H
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index c2957dd294..f6b97262c3 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qstring.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qdatetime.h>
-#include "qjsengine.h"
#include "qjsvalue.h"
+#include "qjsprimitivevalue.h"
+#include "qjsmanagedvalue.h"
#include "qjsvalue_p.h"
#include "qv4value_p.h"
#include "qv4object_p.h"
@@ -54,6 +19,9 @@
#include <private/qv4mm_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmetaobjectwrapper_p.h>
+#include <private/qv4urlobject_p.h>
+#include <private/qqmlbuiltins_p.h>
/*!
\since 5.0
@@ -139,6 +107,16 @@
integers.append(jsArray.property(i).toInt());
}
\endcode
+
+ \section2 Converting to JSON
+
+ It's possible to convert a QJSValue to a JSON type. For example,
+ to convert to an array, use \l QJSEngine::fromScriptValue():
+
+ \code
+ const QJsonValue jsonValue = engine.fromScriptValue<QJsonValue>(jsValue);
+ const QJsonArray jsonArray = jsonValue.toArray();
+ \endcode
*/
/*!
@@ -184,6 +162,18 @@
provided is malformed.
*/
+/*!
+ \enum QJSValue::ObjectConversionBehavior
+
+ This enum is used to specify how JavaScript objects and symbols without an equivalent
+ native Qt type should be treated when converting to QVariant.
+
+ \value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted.
+ Symbols are converted to QString.
+
+ \value RetainJSObjects The value is retained as QJSValue wrapped in QVariant.
+*/
+
QT_BEGIN_NAMESPACE
using namespace QV4;
@@ -191,76 +181,59 @@ using namespace QV4;
/*!
Constructs a new QJSValue with a boolean \a value.
*/
-QJSValue::QJSValue(bool value)
+QJSValue::QJSValue(bool value) : d(QJSValuePrivate::encode(value))
{
- QJSValuePrivate::setVariant(this, QVariant(value));
-}
-
-/*!
- \internal
-*/
-QJSValue::QJSValue(ExecutionEngine *e, quint64 val)
-{
- QJSValuePrivate::setValue(this, e, val);
}
/*!
Constructs a new QJSValue with a number \a value.
*/
-QJSValue::QJSValue(int value)
+QJSValue::QJSValue(int value) : d(QJSValuePrivate::encode(value))
{
- QJSValuePrivate::setVariant(this, QVariant(value));
}
/*!
Constructs a new QJSValue with a number \a value.
*/
-QJSValue::QJSValue(uint value)
+QJSValue::QJSValue(uint value) : d(QJSValuePrivate::encode(value))
{
- QJSValuePrivate::setVariant(this, QVariant((double)value));
}
/*!
Constructs a new QJSValue with a number \a value.
*/
-QJSValue::QJSValue(double value)
+QJSValue::QJSValue(double value) : d(QJSValuePrivate::encode(value))
{
- QJSValuePrivate::setVariant(this, QVariant(value));
}
/*!
Constructs a new QJSValue with a string \a value.
*/
-QJSValue::QJSValue(const QString& value)
+QJSValue::QJSValue(const QString &value) : d(QJSValuePrivate::encode(value))
{
- QJSValuePrivate::setVariant(this, QVariant(value));
}
/*!
Constructs a new QJSValue with a special \a value.
*/
QJSValue::QJSValue(SpecialValue value)
- : d(0)
+ : d(value == NullValue ? QJSValuePrivate::encodeNull() : QJSValuePrivate::encodeUndefined())
{
- if (value == NullValue)
- QJSValuePrivate::setVariant(this, QVariant::fromValue(nullptr));
}
/*!
Constructs a new QJSValue with a string \a value.
*/
-QJSValue::QJSValue(const QLatin1String &value)
+QJSValue::QJSValue(const QLatin1String &value) : d(QJSValuePrivate::encode(value))
{
- QJSValuePrivate::setVariant(this, QVariant(value));
}
/*!
Constructs a new QJSValue with a string \a value.
*/
#ifndef QT_NO_CAST_FROM_ASCII
-QJSValue::QJSValue(const char *value)
+QJSValue::QJSValue(const char *value) : d(QJSValuePrivate::encode(QString::fromUtf8(value)))
{
- QJSValuePrivate::setVariant(this, QVariant(QString::fromUtf8(value)));
}
#endif
@@ -271,14 +244,23 @@ QJSValue::QJSValue(const char *value)
true), then only a reference to the underlying object is copied into
the new script value (i.e., the object itself is not copied).
*/
-QJSValue::QJSValue(const QJSValue& other)
- : d(0)
+QJSValue::QJSValue(const QJSValue &other) : d(other.d)
{
- QV4::Value *v = QJSValuePrivate::getValue(&other);
- if (v) {
- QJSValuePrivate::setValue(this, QJSValuePrivate::engine(&other), *v);
- } else if (QVariant *v = QJSValuePrivate::getVariant(&other)) {
- QJSValuePrivate::setVariant(this, *v);
+ switch (QJSValuePrivate::tag(d)) {
+ case QJSValuePrivate::Kind::Undefined:
+ case QJSValuePrivate::Kind::Null:
+ case QJSValuePrivate::Kind::IntValue:
+ case QJSValuePrivate::Kind::BoolValue:
+ return;
+ case QJSValuePrivate::Kind::DoublePtr:
+ d = QJSValuePrivate::encode(*QJSValuePrivate::doublePtr(d));
+ return;
+ case QJSValuePrivate::Kind::QV4ValuePtr:
+ d = QJSValuePrivate::encode(*QJSValuePrivate::qv4ValuePtr(d));
+ return;
+ case QJSValuePrivate::Kind::QStringPtr:
+ d = QJSValuePrivate::encode(*QJSValuePrivate::qStringPtr(d));
+ break;
}
}
@@ -310,11 +292,7 @@ QJSValue::~QJSValue()
*/
bool QJSValue::isBool() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (val)
- return val->isBoolean();
- QVariant *variant = QJSValuePrivate::getVariant(this);
- return variant && variant->type() == QVariant::Bool;
+ return QJSValuePrivate::tag(d) == QJSValuePrivate::Kind::BoolValue;
}
/*!
@@ -325,27 +303,15 @@ bool QJSValue::isBool() const
*/
bool QJSValue::isNumber() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (val)
- return val->isNumber();
- QVariant *variant = QJSValuePrivate::getVariant(this);
- if (!variant)
- return false;
-
- switch (variant->userType()) {
- case QMetaType::Double:
- case QMetaType::Int:
- case QMetaType::UInt:
- case QMetaType::Long:
- case QMetaType::ULong:
- case QMetaType::Short:
- case QMetaType::UShort:
- case QMetaType::LongLong:
- case QMetaType::ULongLong:
+ switch (QJSValuePrivate::tag(d)) {
+ case QJSValuePrivate::Kind::IntValue:
+ case QJSValuePrivate::Kind::DoublePtr:
return true;
default:
- return false;
+ break;
}
+
+ return false;
}
/*!
@@ -354,14 +320,7 @@ bool QJSValue::isNumber() const
*/
bool QJSValue::isNull() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (val)
- return val->isNull();
- QVariant *variant = QJSValuePrivate::getVariant(this);
- if (!variant)
- return false;
- const int type = variant->userType();
- return type == QMetaType::Nullptr || type == QMetaType::VoidStar;
+ return QJSValuePrivate::tag(d) == QJSValuePrivate::Kind::Null;
}
/*!
@@ -372,24 +331,35 @@ bool QJSValue::isNull() const
*/
bool QJSValue::isString() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (val)
- return val->isString();
- QVariant *variant = QJSValuePrivate::getVariant(this);
- return variant && variant->userType() == QMetaType::QString;
+ switch (QJSValuePrivate::tag(d)) {
+ case QJSValuePrivate::Kind::QStringPtr:
+ return true;
+ case QJSValuePrivate::Kind::QV4ValuePtr: {
+ return QJSValuePrivate::qv4ValuePtr(d)->isString();
+ }
+ default:
+ break;
+ }
+
+ return false;
}
/*!
- Returns true if this QJSValue is of the primitive type Undefined;
- otherwise returns false.
+ Returns true if this QJSValue is of the primitive type Undefined or if the managed value
+ has been cleared (by deleting the engine). Otherwise returns false.
*/
bool QJSValue::isUndefined() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (val)
- return val->isUndefined();
- QVariant *variant = QJSValuePrivate::getVariant(this);
- return !variant || variant->userType() == QMetaType::UnknownType || variant->userType() == QMetaType::Void;
+ switch (QJSValuePrivate::tag(d)) {
+ case QJSValuePrivate::Kind::Undefined:
+ return true;
+ case QJSValuePrivate::Kind::QV4ValuePtr:
+ return QJSValuePrivate::qv4ValuePtr(d)->isUndefined();
+ default:
+ break;
+ }
+
+ return false;
}
/*!
@@ -400,10 +370,19 @@ bool QJSValue::isUndefined() const
*/
bool QJSValue::isError() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return false;
- return val->as<ErrorObject>();
+ return QJSValuePrivate::asManagedType<ErrorObject>(this);
+}
+
+/*!
+ Returns true if this QJSValue is an object of the URL JavaScript class;
+ otherwise returns false.
+
+ \note For a QJSValue that contains a QUrl, this function returns false.
+ However, \c{toVariant().value<QUrl>()} works in both cases.
+*/
+bool QJSValue::isUrl() const
+{
+ return QJSValuePrivate::asManagedType<UrlObject>(this);
}
/*!
@@ -415,10 +394,7 @@ bool QJSValue::isError() const
*/
QJSValue::ErrorType QJSValue::errorType() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return NoError;
- QV4::ErrorObject *error = val->as<ErrorObject>();
+ const QV4::ErrorObject *error = QJSValuePrivate::asManagedType<ErrorObject>(this);
if (!error)
return NoError;
switch (error->d()->errorType) {
@@ -437,8 +413,7 @@ QJSValue::ErrorType QJSValue::errorType() const
case QV4::Heap::ErrorObject::URIError:
return URIError;
}
- Q_UNREACHABLE();
- return NoError;
+ Q_UNREACHABLE_RETURN(NoError);
}
/*!
@@ -449,10 +424,7 @@ QJSValue::ErrorType QJSValue::errorType() const
*/
bool QJSValue::isArray() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return false;
- return val->as<ArrayObject>();
+ return QJSValuePrivate::asManagedType<ArrayObject>(this);
}
/*!
@@ -466,39 +438,46 @@ bool QJSValue::isArray() const
*/
bool QJSValue::isObject() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return false;
- return val->as<QV4::Object>();
+ return QJSValuePrivate::asManagedType<QV4::Object>(this);
}
/*!
- Returns true if this QJSValue can be called a function, otherwise
+ Returns true if this QJSValue is a function, otherwise
returns false.
\sa call()
*/
bool QJSValue::isCallable() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return false;
- return val->as<FunctionObject>();
+ return QJSValuePrivate::asManagedType<FunctionObject>(this);
}
+#if QT_DEPRECATED_SINCE(6, 9)
/*!
+ \deprecated [6.9]
Returns true if this QJSValue is a variant value;
otherwise returns false.
+ \warning This function is likely to give unexpected results.
+ A variant value is only constructed by the QJSEngine in a very
+ limited number of cases. This used to be different before Qt
+ 5.14, where \l{QJSEngine::toScriptValue} would have created
+ them for more types instead of corresponding ECMAScript types.
+ You can get a valid \l QVariant via \l toVariant for many values
+ for which \c{isVariant} returns false.
+
\sa toVariant()
*/
bool QJSValue::isVariant() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return false;
- return val->as<QV4::VariantObject>();
+ if (QJSValuePrivate::asManagedType<QV4::VariantObject>(this))
+ return true;
+ if (auto vt = QJSValuePrivate::asManagedType<QV4::QQmlValueTypeWrapper>(this))
+ if (vt->metaObject() == &QQmlVarForeign::staticMetaObject)
+ return true;
+ return false;
}
+#endif
/*!
Returns the string value of this QJSValue, as defined in
@@ -514,27 +493,22 @@ bool QJSValue::isVariant() const
*/
QString QJSValue::toString() const
{
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch);
-
- if (!val) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- Q_ASSERT(variant);
- if (variant->type() == QVariant::Map)
- return QStringLiteral("[object Object]");
- else if (variant->type() == QVariant::List) {
- const QVariantList list = variant->toList();
- QString result;
- for (int i = 0; i < list.count(); ++i) {
- if (i > 0)
- result.append(QLatin1Char(','));
- result.append(list.at(i).toString());
- }
- return result;
- }
- return variant->toString();
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return *string;
+
+ return QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)).toQStringNoThrow();
+}
+
+template<typename T>
+T caughtResult(const QJSValue *v, T (QV4::Value::*convert)() const)
+{
+ const T result = (QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(v)).*convert)();
+ QV4::ExecutionEngine *engine = QJSValuePrivate::engine(v);
+ if (engine && engine->hasException) {
+ engine->catchException();
+ return T();
}
- return val->toQStringNoThrow();
+ return result;
}
/*!
@@ -551,28 +525,10 @@ QString QJSValue::toString() const
*/
double QJSValue::toNumber() const
{
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch);
-
- if (!val) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- Q_ASSERT(variant);
-
- if (variant->type() == QVariant::String)
- return RuntimeHelpers::stringToNumber(variant->toString());
- else if (variant->canConvert<double>())
- return variant->value<double>();
- else
- return std::numeric_limits<double>::quiet_NaN();
- }
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return RuntimeHelpers::stringToNumber(*string);
- double dbl = val->toNumber();
- QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this);
- if (engine && engine->hasException) {
- engine->catchException();
- return 0;
- }
- return dbl;
+ return caughtResult<double>(this, &QV4::Value::toNumber);
}
/*!
@@ -589,24 +545,10 @@ double QJSValue::toNumber() const
*/
bool QJSValue::toBool() const
{
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch);
-
- if (!val) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- if (variant->userType() == QMetaType::QString)
- return variant->toString().length() > 0;
- else
- return variant->toBool();
- }
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return string->size() > 0;
- bool b = val->toBoolean();
- QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this);
- if (engine && engine->hasException) {
- engine->catchException();
- return false;
- }
- return b;
+ return caughtResult<bool>(this, &QV4::Value::toBoolean);
}
/*!
@@ -623,24 +565,10 @@ bool QJSValue::toBool() const
*/
qint32 QJSValue::toInt() const
{
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch);
-
- if (!val) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- if (variant->userType() == QMetaType::QString)
- return QV4::Value::toInt32(RuntimeHelpers::stringToNumber(variant->toString()));
- else
- return variant->toInt();
- }
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return QV4::Value::toInt32(RuntimeHelpers::stringToNumber(*string));
- qint32 i = val->toInt32();
- QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this);
- if (engine && engine->hasException) {
- engine->catchException();
- return 0;
- }
- return i;
+ return caughtResult<qint32>(this, &QV4::Value::toInt32);
}
/*!
@@ -657,30 +585,29 @@ qint32 QJSValue::toInt() const
*/
quint32 QJSValue::toUInt() const
{
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch);
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return QV4::Value::toUInt32(RuntimeHelpers::stringToNumber(*string));
- if (!val) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- if (variant->userType() == QMetaType::QString)
- return QV4::Value::toUInt32(RuntimeHelpers::stringToNumber(variant->toString()));
- else
- return variant->toUInt();
- }
+ return caughtResult<quint32>(this, &QV4::Value::toUInt32);
+}
- quint32 u = val->toUInt32();
- QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this);
- if (engine && engine->hasException) {
- engine->catchException();
- return 0;
- }
- return u;
+/*!
+ \overload
+
+ Returns toVariant(ConvertJSObjects).
+
+ \sa isVariant()
+*/
+QVariant QJSValue::toVariant() const
+{
+ return toVariant(ConvertJSObjects);
}
/*!
- Returns the QVariant value of this QJSValue, if it can be
- converted to a QVariant; otherwise returns an invalid QVariant.
- The conversion is performed according to the following table:
+ Returns the QVariant value of this QJSValue, if it can be
+ converted to a QVariant; otherwise returns an invalid QVariant.
+ Some JavaScript types and objects have native expressions in Qt.
+ Those are converted to their native expressions. For example:
\table
\header \li Input Type \li Result
@@ -692,42 +619,75 @@ quint32 QJSValue::toUInt() const
\row \li QVariant Object \li The result is the QVariant value of the object (no conversion).
\row \li QObject Object \li A QVariant containing a pointer to the QObject.
\row \li Date Object \li A QVariant containing the date value (toDateTime()).
- \row \li RegExp Object \li A QVariant containing the regular expression value.
- \row \li Array Object \li The array is converted to a QVariantList. Each element is converted to a QVariant, recursively; cyclic references are not followed.
- \row \li Object \li The object is converted to a QVariantMap. Each property is converted to a QVariant, recursively; cyclic references are not followed.
+ \row \li RegularExpression Object \li A QVariant containing the regular expression value.
\endtable
- \sa isVariant()
+ For other types the \a behavior parameter is relevant. If
+ \c ConvertJSObjects is given, a best effort but possibly lossy conversion is
+ attempted. Generic JavaScript objects are converted to QVariantMap.
+ JavaScript arrays are converted to QVariantList. Each property or element is
+ converted to a QVariant, recursively; cyclic references are not followed.
+ JavaScript function objects are dropped. If \c RetainJSObjects is given, the
+ QJSValue is wrapped into a QVariant via QVariant::fromValue(). The resulting
+ conversion is lossless but the internal structure of the objects is not
+ immediately accessible.
+
+ \sa isVariant()
*/
-QVariant QJSValue::toVariant() const
+QVariant QJSValue::toVariant(QJSValue::ObjectConversionBehavior behavior) const
{
- QVariant *variant = QJSValuePrivate::getVariant(this);
- if (variant)
- return *variant;
-
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch);
- Q_ASSERT(val);
-
- if (QV4::Object *o = val->as<QV4::Object>())
- return o->engine()->toVariant(*val, /*typeHint*/ -1, /*createJSValueForObjects*/ false);
-
- if (String *s = val->stringValue())
- return QVariant(s->toQString());
- if (val->isBoolean())
- return QVariant(val->booleanValue());
- if (val->isNumber()) {
- if (val->isInt32())
- return QVariant(val->integerValue());
- return QVariant(val->asDouble());
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return QVariant(*string);
+
+ QV4::Value val = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this));
+ if (val.isUndefined())
+ return QVariant();
+ if (val.isNull())
+ return QVariant(QMetaType::fromType<std::nullptr_t>(), nullptr);
+ if (val.isBoolean())
+ return QVariant(val.booleanValue());
+ if (val.isInt32()) // Includes doubles that can be losslessly casted to int
+ return QVariant(val.integerValue());
+ if (val.isNumber())
+ return QVariant(val.doubleValue());
+
+ Q_ASSERT(val.isManaged());
+
+ if (val.isString())
+ return QVariant(val.toQString());
+ if (val.as<QV4::Managed>()) {
+ if (behavior == RetainJSObjects)
+ return QV4::ExecutionEngine::toVariant(
+ val, /*typeHint*/ QMetaType{}, /*createJSValueForObjectsAndSymbols=*/ true);
+ else
+ return QV4::ExecutionEngine::toVariantLossy(val);
}
- if (val->isNull())
- return QVariant(QMetaType::Nullptr, nullptr);
- Q_ASSERT(val->isUndefined());
+
+ Q_ASSERT(false);
return QVariant();
}
/*!
+ * Converts the value to a QJSPrimitiveValue. If the value holds a type
+ * supported by QJSPrimitiveValue, the value is copied. Otherwise the
+ * value is converted to a string, and the string is stored in
+ * QJSPrimitiveValue.
+ *
+ * \note Conversion of a managed value to a string can throw an exception. In
+ * particular, symbols cannot be coerced into strings, or a custom
+ * toString() method may throw. In this case the result is the undefined
+ * value and the engine carries an error after the conversion.
+ */
+QJSPrimitiveValue QJSValue::toPrimitive() const
+{
+ if (const QString *string = QJSValuePrivate::asQString(this))
+ return *string;
+
+ const QV4::Value val = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this));
+ return QV4::ExecutionEngine::createPrimitive(&val);
+}
+
+/*!
Calls this QJSValue as a function, passing \a args as arguments
to the function, and using the globalObject() as the "this"-object.
Returns the value returned from the function.
@@ -742,13 +702,9 @@ QVariant QJSValue::toVariant() const
\sa isCallable(), callWithInstance(), callAsConstructor()
*/
-QJSValue QJSValue::call(const QJSValueList &args)
+QJSValue QJSValue::call(const QJSValueList &args) const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return QJSValue();
-
- FunctionObject *f = val->as<FunctionObject>();
+ const FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(this);
if (!f)
return QJSValue();
@@ -756,23 +712,23 @@ QJSValue QJSValue::call(const QJSValueList &args)
Q_ASSERT(engine);
Scope scope(engine);
- JSCallData jsCallData(scope, args.length());
- *jsCallData->thisObject = engine->globalObject;
+ JSCallArguments jsCallData(scope, args.size());
+ *jsCallData.thisObject = engine->globalObject;
for (int i = 0; i < args.size(); ++i) {
if (!QJSValuePrivate::checkEngine(engine, args.at(i))) {
qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
return QJSValue();
}
- jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
+ jsCallData.args[i] = QJSValuePrivate::convertToReturnedValue(engine, args.at(i));
}
ScopedValue result(scope, f->call(jsCallData));
if (engine->hasException)
result = engine->catchException();
- if (engine->isInterrupted.loadAcquire())
+ if (engine->isInterrupted.loadRelaxed())
result = engine->newErrorObject(QStringLiteral("Interrupted"));
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
}
/*!
@@ -795,13 +751,9 @@ QJSValue QJSValue::call(const QJSValueList &args)
\sa call()
*/
-QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args)
+QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args) const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return QJSValue();
-
- FunctionObject *f = val->as<FunctionObject>();
+ const FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(this);
if (!f)
return QJSValue();
@@ -814,23 +766,23 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList
return QJSValue();
}
- JSCallData jsCallData(scope, args.size());
- *jsCallData->thisObject = QJSValuePrivate::convertedToValue(engine, instance);
+ JSCallArguments jsCallData(scope, args.size());
+ *jsCallData.thisObject = QJSValuePrivate::convertToReturnedValue(engine, instance);
for (int i = 0; i < args.size(); ++i) {
if (!QJSValuePrivate::checkEngine(engine, args.at(i))) {
qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
return QJSValue();
}
- jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
+ jsCallData.args[i] = QJSValuePrivate::convertToReturnedValue(engine, args.at(i));
}
ScopedValue result(scope, f->call(jsCallData));
if (engine->hasException)
result = engine->catchException();
- if (engine->isInterrupted.loadAcquire())
+ if (engine->isInterrupted.loadRelaxed())
result = engine->newErrorObject(QStringLiteral("Interrupted"));
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
}
/*!
@@ -851,13 +803,9 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList
\sa call(), QJSEngine::newObject()
*/
-QJSValue QJSValue::callAsConstructor(const QJSValueList &args)
+QJSValue QJSValue::callAsConstructor(const QJSValueList &args) const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (!val)
- return QJSValue();
-
- FunctionObject *f = val->as<FunctionObject>();
+ const FunctionObject *f = QJSValuePrivate::asManagedType<FunctionObject>(this);
if (!f)
return QJSValue();
@@ -865,43 +813,24 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args)
Q_ASSERT(engine);
Scope scope(engine);
- JSCallData jsCallData(scope, args.size());
+ JSCallArguments jsCallData(scope, args.size());
for (int i = 0; i < args.size(); ++i) {
if (!QJSValuePrivate::checkEngine(engine, args.at(i))) {
qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine");
return QJSValue();
}
- jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
+ jsCallData.args[i] = QJSValuePrivate::convertToReturnedValue(engine, args.at(i));
}
ScopedValue result(scope, f->callAsConstructor(jsCallData));
if (engine->hasException)
result = engine->catchException();
- if (engine->isInterrupted.loadAcquire())
+ if (engine->isInterrupted.loadRelaxed())
result = engine->newErrorObject(QStringLiteral("Interrupted"));
- return QJSValue(engine, result->asReturnedValue());
-}
-
-#ifdef QT_DEPRECATED
-
-/*!
- \obsolete
-
- Returns the QJSEngine that created this QJSValue,
- or 0 if this QJSValue is invalid or the value is not
- associated with a particular engine.
-*/
-QJSEngine* QJSValue::engine() const
-{
- QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this);
- if (engine)
- return engine->jsEngine();
- return nullptr;
+ return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
}
-#endif // QT_DEPRECATED
-
/*!
If this QJSValue is an object, returns the internal prototype
(\c{__proto__} property) of this object; otherwise returns an
@@ -915,13 +844,13 @@ QJSValue QJSValue::prototype() const
if (!engine)
return QJSValue();
QV4::Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<QV4::Object>());
+ ScopedObject o(scope, QJSValuePrivate::asManagedType<QV4::Object>(this));
if (!o)
return QJSValue();
ScopedObject p(scope, o->getPrototypeOf());
if (!p)
return QJSValue(NullValue);
- return QJSValue(o->internalClass()->engine, p.asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(p.asReturnedValue());
}
/*!
@@ -942,14 +871,11 @@ void QJSValue::setPrototype(const QJSValue& prototype)
if (!engine)
return;
Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return;
- QV4::Value scratch;
- QV4::Value *val = QJSValuePrivate::valueForData(&prototype, &scratch);
- if (!val)
- return;
- if (val->isNull()) {
+ QV4::Value val = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&prototype));
+ if (val.isNull()) {
o->setPrototypeOf(nullptr);
return;
}
@@ -980,15 +906,55 @@ QJSValue& QJSValue::operator=(const QJSValue& other)
QJSValuePrivate::free(this);
d = 0;
- QV4::Value *v = QJSValuePrivate::getValue(&other);
- if (v) {
- QJSValuePrivate::setValue(this, QJSValuePrivate::engine(&other), *v);
- } else if (QVariant *v = QJSValuePrivate::getVariant(&other)) {
- QJSValuePrivate::setVariant(this, *v);
- }
+ if (const QString *string = QJSValuePrivate::asQString(&other))
+ QJSValuePrivate::setString(this, *string);
+ else
+ QJSValuePrivate::setValue(this, QJSValuePrivate::asReturnedValue(&other));
+
return *this;
}
+QJSValue::QJSValue(QJSPrimitiveValue &&value)
+{
+ switch (value.type()) {
+ case QJSPrimitiveValue::Undefined:
+ d = QJSValuePrivate::encodeUndefined();
+ return;
+ case QJSPrimitiveValue::Null:
+ d = QJSValuePrivate::encodeNull();
+ return;
+ case QJSPrimitiveValue::Boolean:
+ d = QJSValuePrivate::encode(value.asBoolean());
+ return;
+ case QJSPrimitiveValue::Integer:
+ d = QJSValuePrivate::encode(value.asInteger());
+ return;
+ case QJSPrimitiveValue::Double:
+ d = QJSValuePrivate::encode(value.asDouble());
+ return;
+ case QJSPrimitiveValue::String:
+ d = QJSValuePrivate::encode(value.asString());
+ return;
+ }
+
+ Q_UNREACHABLE();
+}
+
+QJSValue::QJSValue(QJSManagedValue &&value)
+{
+ if (!value.d) {
+ d = QV4::Encode::undefined();
+ } else if (value.d->isManaged()) {
+ // If it's managed, we can adopt the persistent value.
+ QJSValuePrivate::adoptPersistentValue(this, value.d);
+ value.d = nullptr;
+ } else {
+ d = QJSValuePrivate::encode(*value.d);
+ QV4::PersistentValueStorage::free(value.d);
+ value.d = nullptr;
+ }
+}
+
static bool js_equal(const QString &string, const QV4::Value &value)
{
if (String *s = value.stringValue())
@@ -1031,23 +997,17 @@ static bool js_equal(const QString &string, const QV4::Value &value)
*/
bool QJSValue::equals(const QJSValue& other) const
{
- QV4::Value s1, s2;
- QV4::Value *v = QJSValuePrivate::valueForData(this, &s1);
- QV4::Value *ov = QJSValuePrivate::valueForData(&other, &s2);
-
- if (!v) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- Q_ASSERT(variant);
- if (!ov)
- return *variant == *QJSValuePrivate::getVariant(&other);
- if (variant->type() == QVariant::Map || variant->type() == QVariant::List)
- return false;
- return js_equal(variant->toString(), *ov);
- }
- if (!ov)
- return other.equals(*this);
+ if (const QString *string = QJSValuePrivate::asQString(this)) {
+ if (const QString *otherString = QJSValuePrivate::asQString(&other))
+ return *string == *otherString;
+ return js_equal(*string, QJSValuePrivate::asReturnedValue(&other));
+ }
- return Runtime::CompareEqual::call(*v, *ov);
+ if (const QString *otherString = QJSValuePrivate::asQString(&other))
+ return js_equal(*otherString, QJSValuePrivate::asReturnedValue(this));
+
+ return Runtime::CompareEqual::call(QJSValuePrivate::asReturnedValue(this),
+ QJSValuePrivate::asReturnedValue(&other));
}
/*!
@@ -1074,25 +1034,22 @@ bool QJSValue::equals(const QJSValue& other) const
*/
bool QJSValue::strictlyEquals(const QJSValue& other) const
{
- QV4::Value s1, s2;
- QV4::Value *v = QJSValuePrivate::valueForData(this, &s1);
- QV4::Value *ov = QJSValuePrivate::valueForData(&other, &s2);
-
- if (!v) {
- QVariant *variant = QJSValuePrivate::getVariant(this);
- Q_ASSERT(variant);
- if (!ov)
- return *variant == *QJSValuePrivate::getVariant(&other);
- if (variant->type() == QVariant::Map || variant->type() == QVariant::List)
- return false;
- if (String *s = ov->stringValue())
- return variant->toString() == s->toQString();
+ if (const QString *string = QJSValuePrivate::asQString(this)) {
+ if (const QString *otherString = QJSValuePrivate::asQString(&other))
+ return *string == *otherString;
+ if (const String *s = QJSValuePrivate::asManagedType<String>(&other))
+ return *string == s->toQString();
+ return false;
+ }
+
+ if (const QString *otherString = QJSValuePrivate::asQString(&other)) {
+ if (const String *s = QJSValuePrivate::asManagedType<String>(this))
+ return *otherString == s->toQString();
return false;
}
- if (!ov)
- return other.strictlyEquals(*this);
- return RuntimeHelpers::strictEqual(*v, *ov);
+ return RuntimeHelpers::strictEqual(QJSValuePrivate::asReturnedValue(this),
+ QJSValuePrivate::asReturnedValue(&other));
}
/*!
@@ -1119,7 +1076,7 @@ QJSValue QJSValue::property(const QString& name) const
return QJSValue();
QV4::Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return QJSValue();
@@ -1128,7 +1085,7 @@ QJSValue QJSValue::property(const QString& name) const
if (engine->hasException)
result = engine->catchException();
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
}
/*!
@@ -1167,14 +1124,14 @@ QJSValue QJSValue::property(quint32 arrayIndex) const
return QJSValue();
QV4::Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return QJSValue();
QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->get(arrayIndex));
if (engine->hasException)
engine->catchException();
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
}
/*!
@@ -1199,7 +1156,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value)
return;
Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return;
@@ -1209,7 +1166,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value)
}
ScopedString s(scope, engine->newString(name));
- QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
+ QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(engine, value));
o->put(s->toPropertyKey(), v);
if (engine->hasException)
engine->catchException();
@@ -1253,7 +1210,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value)
return;
Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return;
@@ -1262,7 +1219,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value)
return;
}
- QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
+ QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(engine, value));
PropertyKey id = arrayIndex != UINT_MAX ? PropertyKey::fromArrayIndex(arrayIndex) : engine->id_uintMax()->propertyKey();
o->put(id, v);
if (engine->hasException)
@@ -1296,7 +1253,7 @@ bool QJSValue::deleteProperty(const QString &name)
return false;
Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return false;
@@ -1317,7 +1274,7 @@ bool QJSValue::hasProperty(const QString &name) const
return false;
Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return false;
@@ -1338,7 +1295,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const
return false;
Scope scope(engine);
- ScopedObject o(scope, QJSValuePrivate::getValue(this));
+ ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this));
if (!o)
return false;
@@ -1362,7 +1319,7 @@ QObject *QJSValue::toQObject() const
if (!engine)
return nullptr;
QV4::Scope scope(engine);
- QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, QJSValuePrivate::getValue(this));
+ QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, QJSValuePrivate::asReturnedValue(this));
if (!wrapper)
return nullptr;
@@ -1383,7 +1340,7 @@ const QMetaObject *QJSValue::toQMetaObject() const
if (!engine)
return nullptr;
QV4::Scope scope(engine);
- QV4::Scoped<QV4::QMetaObjectWrapper> wrapper(scope, QJSValuePrivate::getValue(this));
+ QV4::Scoped<QV4::QMetaObjectWrapper> wrapper(scope, QJSValuePrivate::asReturnedValue(this));
if (!wrapper)
return nullptr;
@@ -1400,12 +1357,8 @@ const QMetaObject *QJSValue::toQMetaObject() const
*/
QDateTime QJSValue::toDateTime() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- if (val) {
- QV4::DateObject *date = val->as<DateObject>();
- if (date)
- return date->toQDateTime();
- }
+ if (const QV4::DateObject *date = QJSValuePrivate::asManagedType<DateObject>(this))
+ return date->toQDateTime();
return QDateTime();
}
@@ -1415,8 +1368,7 @@ QDateTime QJSValue::toDateTime() const
*/
bool QJSValue::isDate() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- return val && val->as<DateObject>();
+ return QJSValuePrivate::asManagedType<DateObject>(this);
}
/*!
@@ -1425,8 +1377,7 @@ bool QJSValue::isDate() const
*/
bool QJSValue::isRegExp() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- return val && val->as<RegExpObject>();
+ return QJSValuePrivate::asManagedType<RegExpObject>(this);
}
/*!
@@ -1440,8 +1391,7 @@ bool QJSValue::isRegExp() const
*/
bool QJSValue::isQObject() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- return val && val->as<QV4::QObjectWrapper>() != nullptr;
+ return QJSValuePrivate::asManagedType<QV4::QObjectWrapper>(this);
}
/*!
@@ -1454,8 +1404,72 @@ bool QJSValue::isQObject() const
*/
bool QJSValue::isQMetaObject() const
{
- QV4::Value *val = QJSValuePrivate::getValue(this);
- return val && val->as<QV4::QMetaObjectWrapper>() != nullptr;
+ return QJSValuePrivate::asManagedType<QV4::QMetaObjectWrapper>(this);
+}
+
+#ifndef QT_NO_DATASTREAM
+QDataStream &operator<<(QDataStream &stream, const QJSValue &jsv)
+{
+ quint32 isNullOrUndefined = 0;
+ if (jsv.isNull())
+ isNullOrUndefined |= 0x1;
+ if (jsv.isUndefined())
+ isNullOrUndefined |= 0x2;
+ stream << isNullOrUndefined;
+ if (!isNullOrUndefined) {
+ const QVariant v = jsv.toVariant();
+ switch (v.userType()) {
+ case QMetaType::Bool:
+ case QMetaType::Double:
+ case QMetaType::Int:
+ case QMetaType::QString:
+ v.save(stream);
+ break;
+ default:
+ qWarning() << "QDataStream::operator<< was to save a non-trivial QJSValue."
+ << "This is not supported anymore, please stream a QVariant instead.";
+ QVariant().save(stream);
+ break;
+ }
+
+ }
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QJSValue &jsv)
+{
+ quint32 isNullOrUndefined;
+ stream >> isNullOrUndefined;
+
+ if (isNullOrUndefined & 0x1) {
+ jsv = QJSValue(QJSValue::NullValue);
+ } else if (isNullOrUndefined & 0x2) {
+ jsv = QJSValue();
+ } else {
+ QVariant v;
+ v.load(stream);
+
+ switch (v.userType()) {
+ case QMetaType::Bool:
+ jsv = QJSValue(v.toBool());
+ break;
+ case QMetaType::Double:
+ jsv = QJSValue(v.toDouble());
+ break;
+ case QMetaType::Int:
+ jsv = QJSValue(v.toInt());
+ break;
+ case QMetaType::QString:
+ jsv = QJSValue(v.toString());
+ break;
+ default:
+ qWarning() << "QDataStream::operator>> to restore a non-trivial QJSValue."
+ << "This is not supported anymore, please stream a QVariant instead.";
+ break;
+ }
+ }
+ return stream;
}
+#endif
QT_END_NAMESPACE
diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h
index 2f95e0ff31..065f73f666 100644
--- a/src/qml/jsapi/qjsvalue.h
+++ b/src/qml/jsapi/qjsvalue.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QJSVALUE_H
#define QJSVALUE_H
@@ -53,13 +17,16 @@ class QVariant;
class QObject;
struct QMetaObject;
class QDateTime;
+class QJSPrimitiveValue;
typedef QList<QJSValue> QJSValueList;
namespace QV4 {
struct ExecutionEngine;
- struct Value;
}
+class QJSPrimitiveValue;
+class QJSManagedValue;
+
class Q_QML_EXPORT QJSValue
{
public:
@@ -79,16 +46,19 @@ public:
URIError
};
+ enum ObjectConversionBehavior {
+ ConvertJSObjects,
+ RetainJSObjects
+ };
+
public:
QJSValue(SpecialValue value = UndefinedValue);
~QJSValue();
QJSValue(const QJSValue &other);
-#ifdef Q_COMPILER_RVALUE_REFS
inline QJSValue(QJSValue && other) : d(other.d) { other.d = 0; }
inline QJSValue &operator=(QJSValue &&other)
- { qSwap(d, other.d); return *this; }
-#endif
+ { std::swap(d, other.d); return *this; }
QJSValue(bool value);
QJSValue(int value);
@@ -102,12 +72,18 @@ public:
QJSValue &operator=(const QJSValue &other);
+ explicit QJSValue(QJSPrimitiveValue &&value);
+ explicit QJSValue(QJSManagedValue &&value);
+
bool isBool() const;
bool isNumber() const;
bool isNull() const;
bool isString() const;
bool isUndefined() const;
+#if QT_DEPRECATED_SINCE(6, 9)
+ QT_DEPRECATED_VERSION_X_6_9("This might return unexpected results; consult documentation for more information")
bool isVariant() const;
+#endif
bool isQObject() const;
bool isQMetaObject() const;
bool isObject() const;
@@ -115,13 +91,18 @@ public:
bool isRegExp() const;
bool isArray() const;
bool isError() const;
+ bool isUrl() const;
QString toString() const;
double toNumber() const;
qint32 toInt() const;
quint32 toUInt() const;
bool toBool() const;
+
QVariant toVariant() const;
+ QVariant toVariant(ObjectConversionBehavior behavior) const;
+ QJSPrimitiveValue toPrimitive() const;
+
QObject *toQObject() const;
const QMetaObject *toQMetaObject() const;
QDateTime toDateTime() const;
@@ -144,24 +125,25 @@ public:
bool deleteProperty(const QString &name);
bool isCallable() const;
- QJSValue call(const QJSValueList &args = QJSValueList()); // ### Qt6: Make const
- QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); // ### Qt6: Make const
- QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); // ### Qt6: Make const
+ QJSValue call(const QJSValueList &args = QJSValueList()) const;
+ QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()) const;
+ QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()) const;
ErrorType errorType() const;
-#ifdef QT_DEPRECATED
- QT_DEPRECATED QJSEngine *engine() const;
-#endif
- QJSValue(QV4::ExecutionEngine *e, quint64 val);
private:
friend class QJSValuePrivate;
// force compile error, prevent QJSValue(bool) to be called
- QJSValue(void *) Q_DECL_EQ_DELETE;
+ QJSValue(void *) = delete;
- mutable quintptr d;
+ quint64 d;
};
+#ifndef QT_NO_DATASTREAM
+Q_QML_EXPORT QDataStream &operator<<(QDataStream &, const QJSValue &);
+Q_QML_EXPORT QDataStream &operator>>(QDataStream &, QJSValue &);
+#endif
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QJSValue)
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index 2faffffbae..4624652c93 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -56,7 +20,6 @@
#include <private/qv4value_p.h>
#include <private/qv4string_p.h>
#include <private/qv4engine_p.h>
-#include <private/qflagpointer_p.h>
#include <private/qv4mm_p.h>
#include <private/qv4persistent_p.h>
@@ -64,132 +27,344 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QJSValuePrivate
+class QJSValuePrivate
{
+ static constexpr quint64 s_tagBits = 3; // 3 bits mask
+ static constexpr quint64 s_tagMask = (1 << s_tagBits) - 1;
+
+ static constexpr quint64 s_pointerBit = 0x1;
+
public:
- static inline QV4::Value *getValue(const QJSValue *jsval)
+ enum class Kind {
+ Undefined = 0x0,
+ Null = 0x2,
+ IntValue = 0x4,
+ BoolValue = 0x6,
+ DoublePtr = 0x0 | s_pointerBit,
+ QV4ValuePtr = 0x2 | s_pointerBit,
+ QStringPtr = 0x4 | s_pointerBit,
+ };
+
+ static_assert(quint64(Kind::Undefined) <= s_tagMask);
+ static_assert(quint64(Kind::Null) <= s_tagMask);
+ static_assert(quint64(Kind::IntValue) <= s_tagMask);
+ static_assert(quint64(Kind::BoolValue) <= s_tagMask);
+ static_assert(quint64(Kind::DoublePtr) <= s_tagMask);
+ static_assert(quint64(Kind::QV4ValuePtr) <= s_tagMask);
+ static_assert(quint64(Kind::QStringPtr) <= s_tagMask);
+
+ static Kind tag(quint64 raw) { return Kind(raw & s_tagMask); }
+
+#if QT_POINTER_SIZE == 4
+ static void *pointer(quint64 raw)
{
- if (jsval->d & 3)
- return nullptr;
- return reinterpret_cast<QV4::Value *>(jsval->d);
+ Q_ASSERT(quint64(tag(raw)) & s_pointerBit);
+ return reinterpret_cast<void *>(raw >> 32);
}
- static inline QVariant *getVariant(const QJSValue *jsval)
+ static quint64 encodePointer(void *pointer, Kind tag)
{
- if (jsval->d & 1)
- return reinterpret_cast<QVariant *>(jsval->d & ~3);
- return nullptr;
+ Q_ASSERT(quint64(tag) & s_pointerBit);
+ return (quint64(quintptr(pointer)) << 32) | quint64(tag);
}
+#else
+ static constexpr quint64 s_minAlignment = 1 << s_tagBits;
+ static_assert(alignof(double) >= s_minAlignment);
+ static_assert(alignof(QV4::Value) >= s_minAlignment);
+ static_assert(alignof(QString) >= s_minAlignment);
- static inline void setRawValue(QJSValue *jsval, QV4::Value *v)
+ static void *pointer(quint64 raw)
{
- jsval->d = reinterpret_cast<quintptr>(v);
+ Q_ASSERT(quint64(tag(raw)) & s_pointerBit);
+ return reinterpret_cast<void *>(raw & ~s_tagMask);
}
- static inline void setVariant(QJSValue *jsval, const QVariant &v) {
- QVariant *val = new QVariant(v);
- jsval->d = reinterpret_cast<quintptr>(val) | 1;
+ static quint64 encodePointer(void *pointer, Kind tag)
+ {
+ Q_ASSERT(quint64(tag) & s_pointerBit);
+ return quintptr(pointer) | quint64(tag);
}
+#endif
- static inline void setValue(QJSValue *jsval, QV4::ExecutionEngine *engine, const QV4::Value &v) {
- QV4::Value *value = engine->memoryManager->m_persistentValues->allocate();
- *value = v;
- jsval->d = reinterpret_cast<quintptr>(value);
+ static quint64 encodeUndefined()
+ {
+ return quint64(Kind::Undefined);
}
- static inline void setValue(QJSValue *jsval, QV4::ExecutionEngine *engine, QV4::ReturnedValue v) {
- QV4::Value *value = engine->memoryManager->m_persistentValues->allocate();
- *value = v;
- jsval->d = reinterpret_cast<quintptr>(value);
+ static quint64 encodeNull()
+ {
+ return quint64(Kind::Null);
}
- static QV4::ReturnedValue convertedToValue(QV4::ExecutionEngine *e, const QJSValue &jsval)
+ static int intValue(quint64 v)
{
- QV4::Value *v = getValue(&jsval);
- if (!v) {
- QVariant *variant = getVariant(&jsval);
- v = e->memoryManager->m_persistentValues->allocate();
- *v = variant ? e->fromVariant(*variant) : QV4::Encode::undefined();
- jsval.d = reinterpret_cast<quintptr>(v);
- delete variant;
- }
+ Q_ASSERT(tag(v) == Kind::IntValue);
+ return v >> 32;
+ }
- if (QV4::PersistentValueStorage::getEngine(v) != e) {
- qWarning("JSValue can't be reassigned to another engine.");
- return QV4::Encode::undefined();
- }
+ static quint64 encode(int intValue)
+ {
+ return (quint64(intValue) << 32) | quint64(Kind::IntValue);
+ }
- return v->asReturnedValue();
+ static quint64 encode(uint uintValue)
+ {
+ return (uintValue < uint(std::numeric_limits<int>::max()))
+ ? encode(int(uintValue))
+ : encode(double(uintValue));
}
- static QV4::Value *valueForData(const QJSValue *jsval, QV4::Value *scratch)
+ static bool boolValue(quint64 v)
{
- QV4::Value *v = getValue(jsval);
- if (v)
- return v;
- v = scratch;
- QVariant *variant = getVariant(jsval);
- if (!variant) {
- *v = QV4::Encode::undefined();
- return v;
- }
+ Q_ASSERT(tag(v) == Kind::BoolValue);
+ return v >> 32;
+ }
- switch (variant->userType()) {
- case QMetaType::UnknownType:
- case QMetaType::Void:
- *v = QV4::Encode::undefined();
- break;
- case QMetaType::Nullptr:
- case QMetaType::VoidStar:
- *v = QV4::Encode::null();
- break;
- case QMetaType::Bool:
- *v = QV4::Encode(variant->toBool());
+ static quint64 encode(bool boolValue)
+ {
+ return (quint64(boolValue) << 32) | quint64(Kind::BoolValue);
+ }
+
+ static double *doublePtr(quint64 v)
+ {
+ Q_ASSERT(tag(v) == Kind::DoublePtr);
+ return static_cast<double *>(pointer(v));
+ }
+
+ static quint64 encode(double doubleValue)
+ {
+ return encodePointer(new double(doubleValue), Kind::DoublePtr);
+ }
+
+ static QV4::Value *qv4ValuePtr(quint64 v)
+ {
+ Q_ASSERT(tag(v) == Kind::QV4ValuePtr);
+ return static_cast<QV4::Value *>(pointer(v));
+ }
+
+ static quint64 encode(const QV4::Value &qv4Value)
+ {
+ switch (qv4Value.type()) {
+ case QV4::StaticValue::Boolean_Type:
+ return encode(qv4Value.booleanValue());
+ case QV4::StaticValue::Integer_Type:
+ return encode(qv4Value.integerValue());
+ case QV4::StaticValue::Managed_Type: {
+ auto managed = qv4Value.as<QV4::Managed>();
+ auto engine = managed->engine();
+ auto mm = engine->memoryManager;
+ QV4::Value *m = mm->m_persistentValues->allocate();
+ Q_ASSERT(m);
+ // we create a new strong reference to the heap managed object
+ // to avoid having to rescan the persistent values, we mark it here
+ QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){
+ if constexpr (QV4::WriteBarrier::isInsertionBarrier)
+ managed->heapObject()->mark(stack);
+ });
+ *m = qv4Value;
+ return encodePointer(m, Kind::QV4ValuePtr);
+ }
+ case QV4::StaticValue::Double_Type:
+ return encode(qv4Value.doubleValue());
+ case QV4::StaticValue::Null_Type:
+ return encodeNull();
+ case QV4::StaticValue::Empty_Type:
+ Q_UNREACHABLE();
break;
- case QMetaType::Double:
- *v = QV4::Encode(variant->toDouble());
+ case QV4::StaticValue::Undefined_Type:
break;
- case QMetaType::Int:
- case QMetaType::Short:
- case QMetaType::UShort:
- case QMetaType::Char:
- case QMetaType::UChar:
- *v = QV4::Encode(variant->toInt());
+ }
+
+ return encodeUndefined();
+ }
+
+ static QString *qStringPtr(quint64 v)
+ {
+ Q_ASSERT(tag(v) == Kind::QStringPtr);
+ return static_cast<QString *>(pointer(v));
+ }
+
+ static quint64 encode(QString stringValue)
+ {
+ return encodePointer(new QString(std::move(stringValue)), Kind::QStringPtr);
+ }
+
+ static quint64 encode(QLatin1String stringValue)
+ {
+ return encodePointer(new QString(std::move(stringValue)), Kind::QStringPtr);
+ }
+
+ static QJSValue fromReturnedValue(QV4::ReturnedValue d)
+ {
+ QJSValue result;
+ setValue(&result, d);
+ return result;
+ }
+
+ template<typename T>
+ static const T *asManagedType(const QJSValue *jsval)
+ {
+ if (tag(jsval->d) == Kind::QV4ValuePtr) {
+ if (const QV4::Value *value = qv4ValuePtr(jsval->d))
+ return value->as<T>();
+ }
+ return nullptr;
+ }
+
+ // This is a move operation and transfers ownership.
+ static QV4::Value *takeManagedValue(QJSValue *jsval)
+ {
+ if (tag(jsval->d) == Kind::QV4ValuePtr) {
+ if (QV4::Value *value = qv4ValuePtr(jsval->d)) {
+ jsval->d = encodeUndefined();
+ return value;
+ }
+ }
+ return nullptr;
+ }
+
+ static QV4::ReturnedValue asPrimitiveType(const QJSValue *jsval)
+ {
+ switch (tag(jsval->d)) {
+ case Kind::BoolValue:
+ return QV4::Encode(boolValue(jsval->d));
+ case Kind::IntValue:
+ return QV4::Encode(intValue(jsval->d));
+ case Kind::DoublePtr:
+ return QV4::Encode(*doublePtr(jsval->d));
+ case Kind::Null:
+ return QV4::Encode::null();
+ case Kind::Undefined:
+ case Kind::QV4ValuePtr:
+ case Kind::QStringPtr:
break;
- case QMetaType::UInt:
- *v = QV4::Encode(variant->toUInt());
+ }
+
+ return QV4::Encode::undefined();
+ }
+
+ // Beware: This only returns a non-null string if the QJSValue actually holds one.
+ // QV4::Strings are kept as managed values. Retrieve those with getValue().
+ static const QString *asQString(const QJSValue *jsval)
+ {
+ if (tag(jsval->d) == Kind::QStringPtr) {
+ if (const QString *string = qStringPtr(jsval->d))
+ return string;
+ }
+ return nullptr;
+ }
+
+ static QV4::ReturnedValue asReturnedValue(const QJSValue *jsval)
+ {
+ switch (tag(jsval->d)) {
+ case Kind::BoolValue:
+ return QV4::Encode(boolValue(jsval->d));
+ case Kind::IntValue:
+ return QV4::Encode(intValue(jsval->d));
+ case Kind::DoublePtr:
+ return QV4::Encode(*doublePtr(jsval->d));
+ case Kind::Null:
+ return QV4::Encode::null();
+ case Kind::QV4ValuePtr:
+ return qv4ValuePtr(jsval->d)->asReturnedValue();
+ case Kind::Undefined:
+ case Kind::QStringPtr:
break;
- default:
- return nullptr;
}
- return v;
+
+ return QV4::Encode::undefined();
+ }
+
+ static void setString(QJSValue *jsval, QString s)
+ {
+ jsval->d = encode(std::move(s));
+ }
+
+ // Only use this with an existing persistent value.
+ // Ownership is transferred to the QJSValue.
+ static void adoptPersistentValue(QJSValue *jsval, QV4::Value *v)
+ {
+ jsval->d = encodePointer(v, Kind::QV4ValuePtr);
+ }
+
+ static void setValue(QJSValue *jsval, const QV4::Value &v)
+ {
+ jsval->d = encode(v);
+ }
+
+ // Moves any QString onto the V4 heap, changing the value to reflect that.
+ static void manageStringOnV4Heap(QV4::ExecutionEngine *e, QJSValue *jsval)
+ {
+ if (const QString *string = asQString(jsval)) {
+ jsval->d = encode(QV4::Value::fromHeapObject(e->newString(*string)));
+ delete string;
+ }
+ }
+
+ // Converts any QString on the fly, involving an allocation.
+ // Does not change the value.
+ static QV4::ReturnedValue convertToReturnedValue(QV4::ExecutionEngine *e,
+ const QJSValue &jsval)
+ {
+ if (const QString *string = asQString(&jsval))
+ return e->newString(*string)->asReturnedValue();
+ if (const QV4::Value *val = asManagedType<QV4::Managed>(&jsval)) {
+ if (QV4::PersistentValueStorage::getEngine(val) == e)
+ return val->asReturnedValue();
+
+ qWarning("JSValue can't be reassigned to another engine.");
+ return QV4::Encode::undefined();
+ }
+ return asPrimitiveType(&jsval);
}
- static QV4::ExecutionEngine *engine(const QJSValue *jsval) {
- QV4::Value *v = getValue(jsval);
- return v ? QV4::PersistentValueStorage::getEngine(v) : nullptr;
+ static QV4::ExecutionEngine *engine(const QJSValue *jsval)
+ {
+ if (tag(jsval->d) == Kind::QV4ValuePtr) {
+ if (const QV4::Value *value = qv4ValuePtr(jsval->d))
+ return QV4::PersistentValueStorage::getEngine(value);
+ }
+
+ return nullptr;
}
- static inline bool checkEngine(QV4::ExecutionEngine *e, const QJSValue &jsval) {
+ static bool checkEngine(QV4::ExecutionEngine *e, const QJSValue &jsval)
+ {
QV4::ExecutionEngine *v4 = engine(&jsval);
return !v4 || v4 == e;
}
- static inline void free(QJSValue *jsval) {
- if (QV4::Value *v = QJSValuePrivate::getValue(jsval)) {
- if (QV4::ExecutionEngine *e = engine(jsval)) {
- if (QJSEngine *jsEngine = e->jsEngine()) {
- if (jsEngine->thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(
- jsEngine, [v](){ QV4::PersistentValueStorage::free(v); });
- return;
- }
+ static void free(QJSValue *jsval)
+ {
+ switch (tag(jsval->d)) {
+ case Kind::Undefined:
+ case Kind::Null:
+ case Kind::IntValue:
+ case Kind::BoolValue:
+ return;
+ case Kind::DoublePtr:
+ delete doublePtr(jsval->d);
+ return;
+ case Kind::QStringPtr:
+ delete qStringPtr(jsval->d);
+ return;
+ case Kind::QV4ValuePtr:
+ break;
+ }
+
+ // We need a mutable value for free(). It needs to write to the actual memory.
+ QV4::Value *m = qv4ValuePtr(jsval->d);
+ Q_ASSERT(m); // Otherwise it would have been undefined above.
+ if (QV4::ExecutionEngine *e = QV4::PersistentValueStorage::getEngine(m)) {
+ if (QJSEngine *jsEngine = e->jsEngine()) {
+ if (jsEngine->thread() != QThread::currentThread()) {
+ QMetaObject::invokeMethod(
+ jsEngine, [m](){ QV4::PersistentValueStorage::free(m); });
+ return;
}
}
- QV4::PersistentValueStorage::free(v);
- } else if (QVariant *v = QJSValuePrivate::getVariant(jsval)) {
- delete v;
}
+ QV4::PersistentValueStorage::free(m);
}
};
diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp
index 076b90c5f2..6f561e82ed 100644
--- a/src/qml/jsapi/qjsvalueiterator.cpp
+++ b/src/qml/jsapi/qjsvalueiterator.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qjsvalueiterator.h"
#include "qjsvalueiterator_p.h"
@@ -58,12 +22,12 @@ void QJSValueIteratorPrivate::init(const QJSValue &v)
QV4::ExecutionEngine *e = QJSValuePrivate::engine(&v);
if (!e)
return;
- QV4::Object *o = QJSValuePrivate::getValue(&v)->objectValue();
+ const QV4::Object *o = QJSValuePrivate::asManagedType<QV4::Object>(&v);
if (!o)
return;
engine = e;
- object = o;
+ object.set(e, o->asReturnedValue());
iterator.reset(o->ownPropertyKeys(object.valueRef()));
next();
}
@@ -209,7 +173,7 @@ QJSValue QJSValueIterator::value() const
scope.engine->catchException();
return QJSValue();
}
- return QJSValue(scope.engine, val->asReturnedValue());
+ return QJSValuePrivate::fromReturnedValue(val->asReturnedValue());
}
diff --git a/src/qml/jsapi/qjsvalueiterator.h b/src/qml/jsapi/qjsvalueiterator.h
index f9468a2242..bffc60358d 100644
--- a/src/qml/jsapi/qjsvalueiterator.h
+++ b/src/qml/jsapi/qjsvalueiterator.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QJSVALUEITERATOR_H
#define QJSVALUEITERATOR_H
diff --git a/src/qml/jsapi/qjsvalueiterator_p.h b/src/qml/jsapi/qjsvalueiterator_p.h
index a870850c11..1ac8f22512 100644
--- a/src/qml/jsapi/qjsvalueiterator_p.h
+++ b/src/qml/jsapi/qjsvalueiterator_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QJSVALUEITERATOR_P_H
#define QJSVALUEITERATOR_P_H