diff options
Diffstat (limited to 'src/qml/jsapi')
-rw-r--r-- | src/qml/jsapi/jsapi.pri | 2 | ||||
-rw-r--r-- | src/qml/jsapi/qjsprimitivevalue.cpp | 288 | ||||
-rw-r--r-- | src/qml/jsapi/qjsprimitivevalue.h | 569 | ||||
-rw-r--r-- | src/qml/jsapi/qjsvalue.cpp | 27 | ||||
-rw-r--r-- | src/qml/jsapi/qjsvalue.h | 3 | ||||
-rw-r--r-- | src/qml/jsapi/qjsvalue_p.h | 8 |
6 files changed, 893 insertions, 4 deletions
diff --git a/src/qml/jsapi/jsapi.pri b/src/qml/jsapi/jsapi.pri index f70588ec7b..f96f8c959b 100644 --- a/src/qml/jsapi/jsapi.pri +++ b/src/qml/jsapi/jsapi.pri @@ -1,11 +1,13 @@ SOURCES += \ $$PWD/qjsengine.cpp \ + $$PWD/qjsprimitivevalue.cpp \ $$PWD/qjsvalue.cpp \ $$PWD/qjsvalueiterator.cpp \ HEADERS += \ $$PWD/qjsengine.h \ $$PWD/qjsengine_p.h \ + $$PWD/qjsprimitivevalue.h \ $$PWD/qjsvalue.h \ $$PWD/qjsvalue_p.h \ $$PWD/qjsvalueiterator.h \ diff --git a/src/qml/jsapi/qjsprimitivevalue.cpp b/src/qml/jsapi/qjsprimitivevalue.cpp new file mode 100644 index 0000000000..71fcf5f84a --- /dev/null +++ b/src/qml/jsapi/qjsprimitivevalue.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#include "qjsprimitivevalue.h" + +#include <QtQml/private/qv4runtime_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \since 6.1 + \class QJSPrimitiveUndefined + \brief An empty marker type to signify the JavaScript Undefined type and its single value. + */ + +/*! + \since 6.1 + \class QJSPrimitiveNull + \brief An empty marker type to signify the JavaScript null value. + */ + +/*! + \since 6.1 + \class QJSPrimitiveValue + + \brief The QJSPrimitiveValue class operates on primitive types in JavaScript semantics. + + \ingroup qtjavascript + \inmodule QtQml + + QJSPrimitiveValue supports most of the primitive types defined in the + \l{ECMA-262} standard, in particular Undefined, Boolean, Number, and String. + Additionally, you can store a JavaScript null in a QJSPrimitiveValue and as a + special case of Number, you can store an integer value. + + All those values are stored immediately, without interacting with the + JavaScript heap. Therefore, you can pass QJSPrimitiveValues between different + JavaScript engines. In contrast to QJSManagedValue, there is also no danger + in destroying a QJSPrimitiveValue from a different thread than it was created + in. On the flip side, QJSPrimitiveValue does not hold a reference to any + JavaScript engine. + + QJSPrimitiveValue implements the JavaScript arithmetic and comparison + operators on the supported types in JavaScript semantics. Types are coerced + like the JavaScript engine would coerce them if the operators were written + in a JavaScript expression. + + The JavaScript Symbol type is not supported as it is of very limited utility + regarding arithmetic and comparison operators, the main purpose of + QJSPrimitiveValue. In particular, it causes an exception whenever you try to + coerce it to a number or a string, and we cannot throw exceptions without a + JavaScript Engine. + */ + +/*! + \enum QJSPrimitiveValue::Type + + This enum speicifies the types a QJSPrimitiveValue might contain. + + \value Undefined The JavaScript Undefined value. + \value Null The JavaScript null value. This is in fact not a separate + JavaScript type but a special value of the Object type. As it is + very common and storable without JavaScript engine, it is still + supported. + \value Boolean A JavaScript Boolean value. + \value Integer An integer. This is a special case of the JavaScript Number + type. JavaScript does not have an actual integer type, but + the \l{ECMA-262} standard contains rules on how to transform a + Number in order to prepare it for certain operators that only + make sense on integers, in particular the bit shift operators. + QJSPrimitiveValue's Integer type represents the result of such + a transformation. + \value Double A JavaScript Number value. + \value String A JavaScript String value. + */ + +/*! + \fn Type QJSPrimitiveValue::type() const + + Returns the type of the QJSPrimitiveValue. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue() + + Creates a QJSPrimitiveValue of type Undefined. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue(QJSPrimitiveUndefined undefined) + + Creates a QJSPrimitiveValue of value \a undefined and type Undefined. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue(QJSPrimitiveNull null) + + Creates a QJSPrimitiveValue of value \a null and type Null. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue(bool value) + + Creates a QJSPrimitiveValue of value \a value and type Boolean. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue(int value) + + Creates a QJSPrimitiveValue of value \a value and type Integer. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue(double value) + + Creates a QJSPrimitiveValue of value \a value and type Double. + */ + +/*! + \fn QJSPrimitiveValue::QJSPrimitiveValue(QString value) + + Creates a QJSPrimitiveValue of value \a value and type String. + */ + +/*! + \fn bool QJSPrimitiveValue::toBoolean() const + + Returns the value coerced a boolean by JavaScript rules. + */ + +/*! + \fn int QJSPrimitiveValue::toInteger() const + + Returns the value coerced to an integral 32bit number by the rules JavaScript + would apply when preparing it for a bit shift operation. + */ + +/*! + \fn double QJSPrimitiveValue::toDouble() const + + Returns the value coerced to a JavaScript Number by JavaScript rules. + */ + +/*! + \fn QString QJSPrimitiveValue::toString() const + + Returns the value coerced to a JavaScript String by JavaScript rules. + */ + +/*! + \fn QJSPrimitiveValue operator+(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Perfoms the JavaScript '+' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn QJSPrimitiveValue operator-(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '-' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn QJSPrimitiveValue operator*(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '*' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn QJSPrimitiveValue operator/(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '/' operation between \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn bool QJSPrimitiveValue::strictlyEquals(const QJSPrimitiveValue &other) const + + Performs the JavaScript '===' operation on this QJSPrimitiveValue and + \a other, and returns the result. + */ + +/*! + \fn bool QJSPrimitiveValue::equals(const QJSPrimitiveValue &other) const + + Performs the JavaScript '==' operation on this QJSPrimitiveValue and + \a other, and returns the result. + */ + +/*! + \fn bool operator==(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '===' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn bool operator!=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '!==' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn bool operator<(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '<' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn bool operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '>' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn bool operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '<=' operation on \a lhs and \a rhs, and returns the + result. + */ + +/*! + \fn bool operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + \since 6.1 + + Performs the JavaScript '>=' operation on \a lhs and \a rhs, and returns the + result. + */ + +QString QJSPrimitiveValue::toString(double d) +{ + QString result; + QV4::RuntimeHelpers::numberToString(&result, d); + return result; +} + +QT_END_NAMESPACE + diff --git a/src/qml/jsapi/qjsprimitivevalue.h b/src/qml/jsapi/qjsprimitivevalue.h new file mode 100644 index 0000000000..1a12104a61 --- /dev/null +++ b/src/qml/jsapi/qjsprimitivevalue.h @@ -0,0 +1,569 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef QJSPRIMITIVEVALUE_H +#define QJSPRIMITIVEVALUE_H + +#include <QtQml/qtqmlglobal.h> +#include <QtQml/qjsnumbercoercion.h> + +#include <QtCore/qstring.h> +#include <QtCore/qnumeric.h> + +#include <variant> + +QT_BEGIN_NAMESPACE + +struct QJSPrimitiveUndefined {}; +struct QJSPrimitiveNull {}; + +class QJSPrimitiveValue +{ + template<typename Concrete> + struct StringNaNOperators + { + static constexpr double op(const QString &, QJSPrimitiveUndefined) + { + return std::numeric_limits<double>::quiet_NaN(); + } + + static constexpr double op(QJSPrimitiveUndefined, const QString &) + { + return std::numeric_limits<double>::quiet_NaN(); + } + + static double op(const QString &lhs, QJSPrimitiveNull) { return op(lhs, 0); } + static double op(QJSPrimitiveNull, const QString &rhs) { return op(0, rhs); } + + template<typename T> + static double op(const QString &lhs, T rhs) + { + return Concrete::op(fromString(lhs).toDouble(), rhs); + } + + template<typename T> + static double op(T lhs, const QString &rhs) + { + return Concrete::op(lhs, fromString(rhs).toDouble()); + } + + static double op(const QString &lhs, const QString &rhs) + { + return Concrete::op(fromString(lhs).toDouble(), fromString(rhs).toDouble()); + } + }; + + struct AddOperators { + static constexpr double op(double lhs, double rhs) { return lhs + rhs; } + static bool opOverflow(int lhs, int rhs, int *result) + { + return qAddOverflow(lhs, rhs, result); + } + + template<typename T> + static QString op(const QString &lhs, T rhs) + { + return lhs + QJSPrimitiveValue(rhs).toString(); + } + + template<typename T> + static QString op(T lhs, const QString &rhs) + { + return QJSPrimitiveValue(lhs).toString() + rhs; + } + + static QString op(const QString &lhs, const QString &rhs) { return lhs + rhs; } + }; + + struct SubOperators : private StringNaNOperators<SubOperators> { + static constexpr double op(double lhs, double rhs) { return lhs - rhs; } + static bool opOverflow(int lhs, int rhs, int *result) + { + return qSubOverflow(lhs, rhs, result); + } + + using StringNaNOperators::op; + }; + + struct MulOperators : private StringNaNOperators<MulOperators> { + static constexpr double op(double lhs, double rhs) { return lhs * rhs; } + static bool opOverflow(int lhs, int rhs, int *result) + { + return qMulOverflow(lhs, rhs, result); + } + + using StringNaNOperators::op; + }; + + struct DivOperators : private StringNaNOperators<DivOperators> { + static constexpr double op(double lhs, double rhs) { return lhs / rhs; } + static constexpr bool opOverflow(int, int, int *) + { + return true; + } + + using StringNaNOperators::op; + }; + +public: + enum Type { + Undefined, + Null, + Boolean, + Integer, + Double, + String + }; + + constexpr Type type() const { return Type(d.index()); } + + constexpr QJSPrimitiveValue() = default; + constexpr QJSPrimitiveValue(QJSPrimitiveUndefined undefined) : d(undefined) {} + constexpr QJSPrimitiveValue(QJSPrimitiveNull null) : d(null) {} + constexpr QJSPrimitiveValue(bool value) : d(value) {} + constexpr QJSPrimitiveValue(int value) : d(value) {} + constexpr QJSPrimitiveValue(double value) : d(value) {} + QJSPrimitiveValue(QString string) : d(std::move(string)) {} + + constexpr bool toBoolean() const + { + switch (type()) { + case Undefined: return false; + case Null: return false; + case Boolean: return std::get<bool>(d); + case Integer: return std::get<int>(d) != 0; + case Double: { + const double v = std::get<double>(d); + return !QJSNumberCoercion::equals(v, 0) && !std::isnan(v); + } + case String: return !std::get<QString>(d).isEmpty(); + } + + return false; + } + + constexpr int toInteger() const + { + switch (type()) { + case Undefined: return 0; + case Null: return 0; + case Boolean: return std::get<bool>(d); + case Integer: return std::get<int>(d); + case Double: return QJSNumberCoercion::toInteger(std::get<double>(d)); + case String: return fromString(std::get<String>(d)).toInteger(); + } + + return 0; + } + + constexpr double toDouble() const + { + switch (type()) { + case Undefined: return std::numeric_limits<double>::quiet_NaN(); + case Null: return 0; + case Boolean: return std::get<bool>(d); + case Integer: return std::get<int>(d); + case Double: return std::get<double>(d); + case String: return fromString(std::get<String>(d)).toDouble(); + } + + return std::numeric_limits<double>::quiet_NaN(); + } + + QString toString() const + { + switch (type()) { + case Undefined: return QStringLiteral("undefined"); + case Null: return QStringLiteral("null"); + case Boolean: return std::get<bool>(d) ? QStringLiteral("true") : QStringLiteral("false"); + case Integer: return QString::number(std::get<int>(d)); + case Double: { + const double result = std::get<double>(d); + if (std::isnan(result)) + return QStringLiteral("NaN"); + if (std::isfinite(result)) + return toString(result); + if (result > 0) + return QStringLiteral("Infinity"); + return QStringLiteral("-Infinity"); + } + case String: return std::get<QString>(d); + } + + Q_UNREACHABLE(); + return QString(); + } + + friend inline QJSPrimitiveValue operator+(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + return operate<AddOperators>(lhs, rhs); + } + + friend inline QJSPrimitiveValue operator-(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + return operate<SubOperators>(lhs, rhs); + } + + friend inline QJSPrimitiveValue operator*(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + return operate<MulOperators>(lhs, rhs); + } + + friend inline QJSPrimitiveValue operator/(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + return operate<DivOperators>(lhs, rhs); + } + + constexpr bool strictlyEquals(const QJSPrimitiveValue &other) const + { + const Type myType = type(); + const Type otherType = other.type(); + + if (myType != otherType) { + // int -> double promotion is OK in strict mode + if (myType == Double && otherType == Integer) + return strictlyEquals(double(other.asInteger())); + if (myType == Integer && otherType == Double) + return QJSPrimitiveValue(double(asInteger())).strictlyEquals(other); + return false; + } + + switch (myType) { + case Undefined: + case Null: + return true; + case Boolean: + return asBoolean() == other.asBoolean(); + case Integer: + return asInteger() == other.asInteger(); + case Double: { + const double l = asDouble(); + const double r = other.asDouble(); + if (std::isnan(l) || std::isnan(r)) + return false; + if (qIsNull(l) && qIsNull(r)) + return true; + return QJSNumberCoercion::equals(l, r); + } + case String: + return asString() == other.asString(); + } + + return false; + } + + // Loose operator==, in contrast to strict === + constexpr bool equals(const QJSPrimitiveValue &other) const + { + const Type myType = type(); + const Type otherType = other.type(); + + if (myType == otherType) + return strictlyEquals(other); + + switch (myType) { + case Undefined: + return otherType == Null; + case Null: + return otherType == Undefined; + case Boolean: + return QJSPrimitiveValue(int(asBoolean())).equals(other); + case Integer: + // prefer rhs bool -> int promotion over promoting both to double + return otherType == Boolean + ? QJSPrimitiveValue(asInteger()).equals(int(other.asBoolean())) + : QJSPrimitiveValue(double(asInteger())).equals(other); + case Double: + // Promote the other side to double (or recognize lhs as undefined/null) + return other.equals(*this); + case String: + return fromString(asString()).parsedEquals(other); + } + + return false; + } + + friend constexpr inline bool operator==(const QJSPrimitiveValue &lhs, const + QJSPrimitiveValue &rhs) + { + return lhs.strictlyEquals(rhs); + } + + friend constexpr inline bool operator!=(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + return !lhs.strictlyEquals(rhs); + } + + friend constexpr inline bool operator<(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + switch (lhs.type()) { + case Undefined: + return false; + case Null: { + switch (rhs.type()) { + case Undefined: return false; + case Null: return false; + case Boolean: return 0 < int(rhs.asBoolean()); + case Integer: return 0 < rhs.asInteger(); + case Double: return double(0) < rhs.asDouble(); + case String: return double(0) < rhs.toDouble(); + } + break; + } + case Boolean: { + switch (rhs.type()) { + case Undefined: return false; + case Null: return int(lhs.asBoolean()) < 0; + case Boolean: return lhs.asBoolean() < rhs.asBoolean(); + case Integer: return int(lhs.asBoolean()) < rhs.asInteger(); + case Double: return double(lhs.asBoolean()) < rhs.asDouble(); + case String: return double(lhs.asBoolean()) < rhs.toDouble(); + } + break; + } + case Integer: { + switch (rhs.type()) { + case Undefined: return false; + case Null: return lhs.asInteger() < 0; + case Boolean: return lhs.asInteger() < int(rhs.asBoolean()); + case Integer: return lhs.asInteger() < rhs.asInteger(); + case Double: return double(lhs.asInteger()) < rhs.asDouble(); + case String: return double(lhs.asInteger()) < rhs.toDouble(); + } + break; + } + case Double: { + switch (rhs.type()) { + case Undefined: return false; + case Null: return lhs.asDouble() < double(0); + case Boolean: return lhs.asDouble() < double(rhs.asBoolean()); + case Integer: return lhs.asDouble() < double(rhs.asInteger()); + case Double: return lhs.asDouble() < rhs.asDouble(); + case String: return lhs.asDouble() < rhs.toDouble(); + } + break; + } + case String: { + switch (rhs.type()) { + case Undefined: return false; + case Null: return lhs.toDouble() < double(0); + case Boolean: return lhs.toDouble() < double(rhs.asBoolean()); + case Integer: return lhs.toDouble() < double(rhs.asInteger()); + case Double: return lhs.toDouble() < rhs.asDouble(); + case String: return lhs.asString() < rhs.asString(); + } + break; + } + } + + return false; + } + + friend constexpr inline bool operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + { + return rhs < lhs; + } + + friend constexpr inline bool operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + { + if (lhs.type() == String) { + if (rhs.type() == String) + return lhs.asString() <= rhs.asString(); + else + return fromString(lhs.asString()) <= rhs; + } + if (rhs.type() == String) + return lhs <= fromString(rhs.asString()); + + if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined()) + return false; + return !(lhs > rhs); + } + + friend constexpr inline bool operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + { + if (lhs.type() == String) { + if (rhs.type() == String) + return lhs.asString() >= rhs.asString(); + else + return fromString(lhs.asString()) >= rhs; + } + if (rhs.type() == String) + return lhs >= fromString(rhs.asString()); + + if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined()) + return false; + return !(lhs < rhs); + } + +private: + friend class QJSValue; + + constexpr bool asBoolean() const { return std::get<bool>(d); } + constexpr int asInteger() const { return std::get<int>(d); } + constexpr double asDouble() const { return std::get<double>(d); } + QString asString() const { return std::get<QString>(d); } + + constexpr bool parsedEquals(const QJSPrimitiveValue &other) const + { + return type() != Undefined && equals(other); + } + + static QJSPrimitiveValue fromString(const QString &string) + { + bool ok; + const int intValue = string.toInt(&ok); + if (ok) + return intValue; + + const double doubleValue = string.toDouble(&ok); + if (ok) + return doubleValue; + if (string == QStringLiteral("Infinity")) + return std::numeric_limits<double>::infinity(); + if (string == QStringLiteral("-Infinity")) + return -std::numeric_limits<double>::infinity(); + if (string == QStringLiteral("NaN")) + return std::numeric_limits<double>::quiet_NaN(); + return QJSPrimitiveUndefined(); + } + + static Q_QML_EXPORT QString toString(double d); + + template<typename Operators, typename Lhs, typename Rhs> + static QJSPrimitiveValue operateOnIntegers(const QJSPrimitiveValue &lhs, + const QJSPrimitiveValue &rhs) + { + int result; + if (Operators::opOverflow(std::get<Lhs>(lhs.d), std::get<Rhs>(rhs.d), &result)) + return Operators::op(std::get<Lhs>(lhs.d), std::get<Rhs>(rhs.d)); + return result; + } + + template<typename Operators> + static QJSPrimitiveValue operate(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) + { + switch (lhs.type()) { + case Undefined: + switch (rhs.type()) { + case Undefined: return std::numeric_limits<double>::quiet_NaN(); + case Null: return std::numeric_limits<double>::quiet_NaN(); + case Boolean: return std::numeric_limits<double>::quiet_NaN(); + case Integer: return std::numeric_limits<double>::quiet_NaN(); + case Double: return std::numeric_limits<double>::quiet_NaN(); + case String: return Operators::op(QJSPrimitiveUndefined(), rhs.asString()); + } + break; + case Null: + switch (rhs.type()) { + case Undefined: return std::numeric_limits<double>::quiet_NaN(); + case Null: return operateOnIntegers<Operators, int, int>(0, 0); + case Boolean: return operateOnIntegers<Operators, int, bool>(0, rhs); + case Integer: return operateOnIntegers<Operators, int, int>(0, rhs); + case Double: return Operators::op(0, rhs.asDouble()); + case String: return Operators::op(QJSPrimitiveNull(), rhs.asString()); + } + break; + case Boolean: + switch (rhs.type()) { + case Undefined: return std::numeric_limits<double>::quiet_NaN(); + case Null: return operateOnIntegers<Operators, bool, int>(lhs, 0); + case Boolean: return operateOnIntegers<Operators, bool, bool>(lhs, rhs); + case Integer: return operateOnIntegers<Operators, bool, int>(lhs, rhs); + case Double: return Operators::op(lhs.asBoolean(), rhs.asDouble()); + case String: return Operators::op(lhs.asBoolean(), rhs.asString()); + } + break; + case Integer: + switch (rhs.type()) { + case Undefined: return std::numeric_limits<double>::quiet_NaN(); + case Null: return operateOnIntegers<Operators, int, int>(lhs, 0); + case Boolean: return operateOnIntegers<Operators, int, bool>(lhs, rhs); + case Integer: return operateOnIntegers<Operators, int, int>(lhs, rhs); + case Double: return Operators::op(lhs.asInteger(), rhs.asDouble()); + case String: return Operators::op(lhs.asInteger(), rhs.asString()); + } + break; + case Double: + switch (rhs.type()) { + case Undefined: return std::numeric_limits<double>::quiet_NaN(); + case Null: return Operators::op(lhs.asDouble(), 0); + case Boolean: return Operators::op(lhs.asDouble(), rhs.asBoolean()); + case Integer: return Operators::op(lhs.asDouble(), rhs.asInteger()); + case Double: return Operators::op(lhs.asDouble(), rhs.asDouble()); + case String: return Operators::op(lhs.asDouble(), rhs.asString()); + } + break; + case String: + switch (rhs.type()) { + case Undefined: return Operators::op(lhs.asString(), QJSPrimitiveUndefined()); + case Null: return Operators::op(lhs.asString(), QJSPrimitiveNull()); + case Boolean: return Operators::op(lhs.asString(), rhs.asBoolean()); + case Integer: return Operators::op(lhs.asString(), rhs.asInteger()); + case Double: return Operators::op(lhs.asString(), rhs.asDouble()); + case String: return Operators::op(lhs.asString(), rhs.asString()); + } + break; + } + + Q_UNREACHABLE(); + return QJSPrimitiveUndefined(); + } + + constexpr bool isNanOrUndefined() const + { + switch (type()) { + case Undefined: return true; + case Double: return std::isnan(asDouble()); + default: return false; + } + } + + std::variant<QJSPrimitiveUndefined, QJSPrimitiveNull, bool, int, double, QString> d; +}; + +QT_END_NAMESPACE + +#endif // QJSPRIMITIVEVALUE_H diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index ad5ab95408..ce3ab0076b 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -42,6 +42,7 @@ #include <QtCore/qdatetime.h> #include "qjsengine.h" #include "qjsvalue.h" +#include "qjsprimitivevalue.h" #include "qjsvalue_p.h" #include "qv4value_p.h" #include "qv4object_p.h" @@ -857,6 +858,32 @@ QJSValue& QJSValue::operator=(const QJSValue& other) return *this; } +QJSValue::QJSValue(QJSPrimitiveValue &&value) +{ + switch (value.type()) { + case QJSPrimitiveValue::Undefined: + d = QV4::Encode::undefined(); + return; + case QJSPrimitiveValue::Null: + d = QV4::Encode::null(); + return; + case QJSPrimitiveValue::Boolean: + d = QV4::Encode(value.asBoolean()); + return; + case QJSPrimitiveValue::Integer: + d = QV4::Encode(value.asInteger()); + return; + case QJSPrimitiveValue::Double: + d = QV4::Encode(value.asDouble()); + return; + case QJSPrimitiveValue::String: + QJSValuePrivate::setString(this, std::move(std::get<QString>(value.d))); + return; + } + + Q_UNREACHABLE(); +} + static bool js_equal(const QString &string, const QV4::Value &value) { if (String *s = value.stringValue()) diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index ad3c4ffd4d..393f1c960e 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -53,6 +53,7 @@ class QVariant; class QObject; struct QMetaObject; class QDateTime; +class QJSPrimitiveValue; typedef QList<QJSValue> QJSValueList; namespace QV4 { @@ -107,6 +108,8 @@ public: QJSValue &operator=(const QJSValue &other); + explicit QJSValue(QJSPrimitiveValue &&value); + bool isBool() const; bool isNumber() const; bool isNull() const; diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 5533682144..9e371118cc 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -89,9 +89,9 @@ class Q_AUTOTEST_EXPORT QJSValuePrivate return (m & IsString) ? reinterpret_cast<QString *>(m & ~IsString) : nullptr; } - static QV4::ReturnedValue encode(const QString &string) + static QV4::ReturnedValue encode(QString string) { - const quintptr m = quintptr(new QString(string)) | IsString; + const quintptr m = quintptr(new QString(std::move(string))) | IsString; return encodeRawValue(m); } @@ -160,9 +160,9 @@ public: return QV4::Encode::undefined(); } - static void setString(QJSValue *jsval, const QString &s) + static void setString(QJSValue *jsval, QString s) { - jsval->d = encode(s); + jsval->d = encode(std::move(s)); } static void setValue(QJSValue *jsval, const QV4::Value &v) |