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