diff options
Diffstat (limited to 'src/qml/jsapi/qjsvalue.cpp')
-rw-r--r-- | src/qml/jsapi/qjsvalue.cpp | 238 |
1 files changed, 130 insertions, 108 deletions
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 6d70c72722..5d320809dd 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1,46 +1,9 @@ -/**************************************************************************** -** -** 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" @@ -56,6 +19,8 @@ #include <private/qv4mm_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4urlobject_p.h> +#include <private/qqmlbuiltins_p.h> /*! \since 5.0 @@ -141,6 +106,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 */ /*! @@ -189,10 +164,11 @@ /*! \enum QJSValue::ObjectConversionBehavior - This enum is used to specify how JavaScript objects without an equivalent + 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. */ @@ -204,61 +180,59 @@ using namespace QV4; /*! Constructs a new QJSValue with a boolean \a value. */ -QJSValue::QJSValue(bool value) : d(QV4::Encode(value)) +QJSValue::QJSValue(bool value) : d(QJSValuePrivate::encode(value)) { } /*! Constructs a new QJSValue with a number \a value. */ -QJSValue::QJSValue(int value) : d(QV4::Encode(value)) +QJSValue::QJSValue(int value) : d(QJSValuePrivate::encode(value)) { } /*! Constructs a new QJSValue with a number \a value. */ -QJSValue::QJSValue(uint value) : d(QV4::Encode(value)) +QJSValue::QJSValue(uint value) : d(QJSValuePrivate::encode(value)) { } /*! Constructs a new QJSValue with a number \a value. */ -QJSValue::QJSValue(double value) : d(QV4::Encode(value)) +QJSValue::QJSValue(double value) : d(QJSValuePrivate::encode(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::setString(this, QString(value)); } /*! Constructs a new QJSValue with a special \a value. */ -QJSValue::QJSValue(SpecialValue value) : d(value == NullValue ? QV4::Encode::null() : 0) +QJSValue::QJSValue(SpecialValue value) + : d(value == NullValue ? QJSValuePrivate::encodeNull() : QJSValuePrivate::encodeUndefined()) { } /*! Constructs a new QJSValue with a string \a value. */ -QJSValue::QJSValue(const QLatin1String &value) +QJSValue::QJSValue(const QLatin1String &value) : d(QJSValuePrivate::encode(value)) { - QJSValuePrivate::setString(this, QString(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::setString(this, QString(QString::fromUtf8(value))); } #endif @@ -269,12 +243,24 @@ 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) { - if (const QString *string = QJSValuePrivate::asQString(&other)) - QJSValuePrivate::setString(this, *string); - else - QJSValuePrivate::setValue(this, QJSValuePrivate::asReturnedValue(&other)); + 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; + } } /*! @@ -305,7 +291,7 @@ QJSValue::~QJSValue() */ bool QJSValue::isBool() const { - return QV4::Value::fromReturnedValue(d).isBoolean(); + return QJSValuePrivate::tag(d) == QJSValuePrivate::Kind::BoolValue; } /*! @@ -316,7 +302,15 @@ bool QJSValue::isBool() const */ bool QJSValue::isNumber() const { - return QV4::Value::fromReturnedValue(d).isNumber(); + switch (QJSValuePrivate::tag(d)) { + case QJSValuePrivate::Kind::IntValue: + case QJSValuePrivate::Kind::DoublePtr: + return true; + default: + break; + } + + return false; } /*! @@ -325,7 +319,7 @@ bool QJSValue::isNumber() const */ bool QJSValue::isNull() const { - return QV4::Value::fromReturnedValue(d).isNull(); + return QJSValuePrivate::tag(d) == QJSValuePrivate::Kind::Null; } /*! @@ -336,11 +330,17 @@ bool QJSValue::isNull() const */ bool QJSValue::isString() const { - if (QJSValuePrivate::asQString(this)) + switch (QJSValuePrivate::tag(d)) { + case QJSValuePrivate::Kind::QStringPtr: return true; + case QJSValuePrivate::Kind::QV4ValuePtr: { + return QJSValuePrivate::qv4ValuePtr(d)->isString(); + } + default: + break; + } - // String is managed - return QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)).isString(); + return false; } /*! @@ -349,14 +349,16 @@ bool QJSValue::isString() const */ bool QJSValue::isUndefined() const { - if (QJSValuePrivate::asQString(this)) - return false; - QV4::Value v = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)); - if (v.isUndefined()) + switch (QJSValuePrivate::tag(d)) { + case QJSValuePrivate::Kind::Undefined: return true; - if (!v.isManaged()) - return false; - return v.managed() == nullptr; + case QJSValuePrivate::Kind::QV4ValuePtr: + return QJSValuePrivate::qv4ValuePtr(d)->isUndefined(); + default: + break; + } + + return false; } /*! @@ -371,6 +373,18 @@ bool QJSValue::isError() const } /*! + 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); +} + +/*! \since 5.12 Returns the error type this QJSValue represents if it is an Error object. Otherwise, returns \c NoError." @@ -398,8 +412,7 @@ QJSValue::ErrorType QJSValue::errorType() const case QV4::Heap::ErrorObject::URIError: return URIError; } - Q_UNREACHABLE(); - return NoError; + Q_UNREACHABLE_RETURN(NoError); } /*! @@ -438,16 +451,32 @@ bool QJSValue::isCallable() const 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 { - return QJSValuePrivate::asManagedType<QV4::VariantObject>(this); + 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 @@ -516,7 +545,7 @@ double QJSValue::toNumber() const bool QJSValue::toBool() const { if (const QString *string = QJSValuePrivate::asQString(this)) - return string->length() > 0; + return string->size() > 0; return caughtResult<bool>(this, &QV4::Value::toBoolean); } @@ -625,8 +654,13 @@ QVariant QJSValue::toVariant(QJSValue::ObjectConversionBehavior behavior) const if (val.isString()) return QVariant(val.toQString()); - if (QV4::Managed *m = val.as<QV4::Managed>()) - return m->engine()->toVariant(val, /*typeHint*/ -1, behavior == RetainJSObjects); + if (val.as<QV4::Managed>()) { + if (behavior == RetainJSObjects) + return QV4::ExecutionEngine::toVariant( + val, /*typeHint*/ QMetaType{}, /*createJSValueForObjectsAndSymbols=*/ true); + else + return QV4::ExecutionEngine::toVariantLossy(val); + } Q_ASSERT(false); return QVariant(); @@ -649,20 +683,7 @@ QJSPrimitiveValue QJSValue::toPrimitive() const return *string; const QV4::Value val = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)); - if (val.isUndefined()) - return QJSPrimitiveUndefined(); - if (val.isNull()) - return QJSPrimitiveNull(); - if (val.isBoolean()) - return val.toBoolean(); - if (val.isInteger()) - return val.integerValue(); - if (val.isDouble()) - return val.doubleValue(); - - bool ok; - const QString result = val.toQString(&ok); - return ok ? QJSPrimitiveValue(result) : QJSPrimitiveValue(QJSPrimitiveUndefined()); + return QV4::ExecutionEngine::createPrimitive(&val); } /*! @@ -690,20 +711,20 @@ QJSValue QJSValue::call(const QJSValueList &args) const 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::convertToReturnedValue(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 QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); @@ -744,20 +765,20 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList return QJSValue(); } - JSCallData jsCallData(scope, args.size()); - *jsCallData->thisObject = QJSValuePrivate::convertToReturnedValue(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::convertToReturnedValue(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 QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); @@ -791,19 +812,19 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) const 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::convertToReturnedValue(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 QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); @@ -896,22 +917,22 @@ QJSValue::QJSValue(QJSPrimitiveValue &&value) { switch (value.type()) { case QJSPrimitiveValue::Undefined: - d = QV4::Encode::undefined(); + d = QJSValuePrivate::encodeUndefined(); return; case QJSPrimitiveValue::Null: - d = QV4::Encode::null(); + d = QJSValuePrivate::encodeNull(); return; case QJSPrimitiveValue::Boolean: - d = QV4::Encode(value.asBoolean()); + d = QJSValuePrivate::encode(value.asBoolean()); return; case QJSPrimitiveValue::Integer: - d = QV4::Encode(value.asInteger()); + d = QJSValuePrivate::encode(value.asInteger()); return; case QJSPrimitiveValue::Double: - d = QV4::Encode(value.asDouble()); + d = QJSValuePrivate::encode(value.asDouble()); return; case QJSPrimitiveValue::String: - QJSValuePrivate::setString(this, std::move(std::get<QString>(value.d))); + d = QJSValuePrivate::encode(value.asString()); return; } @@ -923,10 +944,11 @@ QJSValue::QJSValue(QJSManagedValue &&value) if (!value.d) { d = QV4::Encode::undefined(); } else if (value.d->isManaged()) { - QJSValuePrivate::setRawValue(this, value.d); + // If it's managed, we can adopt the persistent value. + QJSValuePrivate::adoptPersistentValue(this, value.d); value.d = nullptr; } else { - d = value.d->asReturnedValue(); + d = QJSValuePrivate::encode(*value.d); QV4::PersistentValueStorage::free(value.d); value.d = nullptr; } |