aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsapi/qjsengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsapi/qjsengine.cpp')
-rw-r--r--src/qml/jsapi/qjsengine.cpp765
1 files changed, 527 insertions, 238 deletions
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"