diff options
Diffstat (limited to 'src/qml/jsapi/qjsvalue.cpp')
-rw-r--r-- | src/qml/jsapi/qjsvalue.cpp | 787 |
1 files changed, 403 insertions, 384 deletions
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 1ab6e8c767..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" @@ -51,10 +16,12 @@ #include "qv4variantobject_p.h" #include "qv4regexpobject_p.h" #include "qv4errorobject_p.h" -#include "private/qv8engine_p.h" #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 @@ -140,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 */ /*! @@ -185,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; @@ -192,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 @@ -272,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; } } @@ -311,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; } /*! @@ -326,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; } /*! @@ -355,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; } /*! @@ -373,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; } /*! @@ -401,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); } /*! @@ -416,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) { @@ -438,8 +413,7 @@ QJSValue::ErrorType QJSValue::errorType() const case QV4::Heap::ErrorObject::URIError: return URIError; } - Q_UNREACHABLE(); - return NoError; + Q_UNREACHABLE_RETURN(NoError); } /*! @@ -450,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); } /*! @@ -467,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 @@ -515,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; } /*! @@ -552,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); } /*! @@ -590,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); } /*! @@ -624,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); } /*! @@ -658,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 @@ -693,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. @@ -743,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(); @@ -757,21 +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.loadRelaxed()) + result = engine->newErrorObject(QStringLiteral("Interrupted")); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -794,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(); @@ -813,21 +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.loadRelaxed()) + result = engine->newErrorObject(QStringLiteral("Interrupted")); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -848,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(); @@ -862,41 +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.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 @@ -910,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()); } /*! @@ -937,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; } @@ -975,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()) @@ -1026,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::method_compareEqual(*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)); } /*! @@ -1069,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)); } /*! @@ -1114,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(); @@ -1123,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()); } /*! @@ -1162,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()); } /*! @@ -1194,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; @@ -1204,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(); @@ -1248,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; @@ -1257,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) @@ -1291,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; @@ -1312,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; @@ -1333,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; @@ -1357,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; @@ -1378,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; @@ -1395,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(); } @@ -1410,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); } /*! @@ -1420,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); } /*! @@ -1435,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); } /*! @@ -1449,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 |