aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsapi/qjsprimitivevalue.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsapi/qjsprimitivevalue.h')
-rw-r--r--src/qml/jsapi/qjsprimitivevalue.h980
1 files changed, 980 insertions, 0 deletions
diff --git a/src/qml/jsapi/qjsprimitivevalue.h b/src/qml/jsapi/qjsprimitivevalue.h
new file mode 100644
index 0000000000..3551eca358
--- /dev/null
+++ b/src/qml/jsapi/qjsprimitivevalue.h
@@ -0,0 +1,980 @@
+// Copyright (C) 2020 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
+
+#ifndef QJSPRIMITIVEVALUE_H
+#define QJSPRIMITIVEVALUE_H
+
+#include <QtQml/qtqmlglobal.h>
+#include <QtQml/qjsnumbercoercion.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qvariant.h>
+
+#include <variant>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 { struct ExecutionEngine; }
+
+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)
+ {
+ // compare mul_int32 in qv4math_p.h
+ auto hadOverflow = qMulOverflow(lhs, rhs, result);
+ if (((lhs < 0) ^ (rhs < 0)) && (*result == 0))
+ return true; // result must be negative 0, does not fit into int
+ return hadOverflow;
+ }
+
+ 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 : quint8 {
+ Undefined,
+ Null,
+ Boolean,
+ Integer,
+ Double,
+ String
+ };
+
+ constexpr Type type() const { return Type(d.type()); }
+
+ // Prevent casting from Type to int
+ QJSPrimitiveValue(Type) = delete;
+
+ Q_IMPLICIT constexpr QJSPrimitiveValue() noexcept = default;
+ Q_IMPLICIT constexpr QJSPrimitiveValue(QJSPrimitiveUndefined undefined) noexcept : d(undefined) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(QJSPrimitiveNull null) noexcept : d(null) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(bool value) noexcept : d(value) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(int value) noexcept : d(value) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValue(double value) noexcept : d(value) {}
+ Q_IMPLICIT QJSPrimitiveValue(QString string) noexcept : d(std::move(string)) {}
+
+ explicit QJSPrimitiveValue(const QMetaType type, const void *value) noexcept
+ {
+ switch (type.id()) {
+ case QMetaType::UnknownType:
+ d = QJSPrimitiveUndefined();
+ break;
+ case QMetaType::Nullptr:
+ d = QJSPrimitiveNull();
+ break;
+ case QMetaType::Bool:
+ d = *static_cast<const bool *>(value);
+ break;
+ case QMetaType::Int:
+ d = *static_cast<const int *>(value);
+ break;
+ case QMetaType::Double:
+ d = *static_cast<const double *>(value);
+ break;
+ case QMetaType::QString:
+ d = *static_cast<const QString *>(value);
+ break;
+ default:
+ // Unsupported. Remains undefined.
+ break;
+ }
+ }
+
+ explicit QJSPrimitiveValue(QMetaType type) noexcept
+ {
+ switch (type.id()) {
+ case QMetaType::UnknownType:
+ d = QJSPrimitiveUndefined();
+ break;
+ case QMetaType::Nullptr:
+ d = QJSPrimitiveNull();
+ break;
+ case QMetaType::Bool:
+ d = false;
+ break;
+ case QMetaType::Int:
+ d = 0;
+ break;
+ case QMetaType::Double:
+ d = 0.0;
+ break;
+ case QMetaType::QString:
+ d = QString();
+ break;
+ default:
+ // Unsupported. Remains undefined.
+ break;
+ }
+ }
+
+ explicit QJSPrimitiveValue(const QVariant &variant) noexcept
+ : QJSPrimitiveValue(variant.metaType(), variant.data())
+ {
+ }
+
+ constexpr QMetaType metaType() const { return d.metaType(); }
+ constexpr void *data() { return d.data(); }
+ constexpr const void *data() const { return d.data(); }
+ constexpr const void *constData() const { return d.data(); }
+
+ template<Type type>
+ QJSPrimitiveValue to() const {
+ if constexpr (type == Undefined)
+ return QJSPrimitiveUndefined();
+ if constexpr (type == Null)
+ return QJSPrimitiveNull();
+ if constexpr (type == Boolean)
+ return toBoolean();
+ if constexpr (type == Integer)
+ return toInteger();
+ if constexpr (type == Double)
+ return toDouble();
+ if constexpr (type == String)
+ return toString();
+
+ Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
+ }
+
+ constexpr bool toBoolean() const
+ {
+ switch (type()) {
+ case Undefined: return false;
+ case Null: return false;
+ case Boolean: return asBoolean();
+ case Integer: return asInteger() != 0;
+ case Double: {
+ const double v = asDouble();
+ return !QJSNumberCoercion::equals(v, 0) && !std::isnan(v);
+ }
+ case String: return !asString().isEmpty();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
+ return false;
+ #endif
+ }
+
+ constexpr int toInteger() const
+ {
+ switch (type()) {
+ case Undefined: return 0;
+ case Null: return 0;
+ case Boolean: return asBoolean();
+ case Integer: return asInteger();
+ case Double: return QJSNumberCoercion::toInteger(asDouble());
+ case String: return fromString(asString()).toInteger();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(0);
+ #else
+ return 0;
+ #endif
+ }
+
+ constexpr double toDouble() const
+ {
+ switch (type()) {
+ case Undefined: return std::numeric_limits<double>::quiet_NaN();
+ case Null: return 0;
+ case Boolean: return asBoolean();
+ case Integer: return asInteger();
+ case Double: return asDouble();
+ case String: return fromString(asString()).toDouble();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN({});
+ #else
+ return {};
+ #endif
+ }
+
+ QString toString() const
+ {
+ switch (type()) {
+ case Undefined: return QStringLiteral("undefined");
+ case Null: return QStringLiteral("null");
+ case Boolean: return asBoolean() ? QStringLiteral("true") : QStringLiteral("false");
+ case Integer: return QString::number(asInteger());
+ case Double: {
+ const double result = asDouble();
+ 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 asString();
+ }
+
+ Q_UNREACHABLE_RETURN(QString());
+ }
+
+ QVariant toVariant() const
+ {
+ switch (type()) {
+ case Undefined: return QVariant();
+ case Null: return QVariant::fromValue<std::nullptr_t>(nullptr);
+ case Boolean: return QVariant(asBoolean());
+ case Integer: return QVariant(asInteger());
+ case Double: return QVariant(asDouble());
+ case String: return QVariant(asString());
+ }
+
+ Q_UNREACHABLE_RETURN(QVariant());
+ }
+
+ 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);
+ }
+
+ friend inline QJSPrimitiveValue operator%(const QJSPrimitiveValue &lhs,
+ const QJSPrimitiveValue &rhs)
+ {
+ switch (lhs.type()) {
+ case Null:
+ case Boolean:
+ case Integer:
+ switch (rhs.type()) {
+ case Boolean:
+ case Integer: {
+ const int leftInt = lhs.toInteger();
+ const int rightInt = rhs.toInteger();
+ if (leftInt >= 0 && rightInt > 0)
+ return leftInt % rightInt;
+ Q_FALLTHROUGH();
+ }
+ case Undefined:
+ case Null:
+ case Double:
+ case String:
+ break;
+ }
+ Q_FALLTHROUGH();
+ case Undefined:
+ case Double:
+ case String:
+ break;
+ }
+
+ return std::fmod(lhs.toDouble(), rhs.toDouble());
+ }
+
+ QJSPrimitiveValue &operator++()
+ {
+ // ++a is modeled as a -= (-1) to avoid the potential string concatenation
+ return (*this = operate<SubOperators>(*this, -1));
+ }
+
+ QJSPrimitiveValue operator++(int)
+ {
+ // a++ is modeled as a -= (-1) to avoid the potential string concatenation
+ QJSPrimitiveValue other = operate<SubOperators>(*this, -1);
+ std::swap(other, *this);
+ return +other; // We still need to coerce the original value.
+ }
+
+ QJSPrimitiveValue &operator--()
+ {
+ return (*this = operate<SubOperators>(*this, 1));
+ }
+
+ QJSPrimitiveValue operator--(int)
+ {
+ QJSPrimitiveValue other = operate<SubOperators>(*this, 1);
+ std::swap(other, *this);
+ return +other; // We still need to coerce the original value.
+ }
+
+ QJSPrimitiveValue operator+()
+ {
+ // +a is modeled as a -= 0. That should force it to number.
+ return (*this = operate<SubOperators>(*this, 0));
+ }
+
+ QJSPrimitiveValue operator-()
+ {
+ return (*this = operate<MulOperators>(*this, -1));
+ }
+
+ 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 QJSManagedValue;
+ friend class QJSValue;
+ friend struct QV4::ExecutionEngine;
+
+ constexpr bool asBoolean() const { return d.getBool(); }
+ constexpr int asInteger() const { return d.getInt(); }
+ constexpr double asDouble() const { return d.getDouble(); }
+ QString asString() const { return d.getString(); }
+
+ 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(lhs.d.get<Lhs>(), rhs.d.get<Rhs>(), &result))
+ return Operators::op(lhs.d.get<Lhs>(), rhs.d.get<Rhs>());
+ 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 Null: return false;
+ case Boolean: return false;
+ case Integer: return false;
+ case Double: return std::isnan(asDouble());
+ case String: return false;
+ }
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
+ return false;
+ #endif
+ }
+
+ struct QJSPrimitiveValuePrivate
+ {
+ // Can't be default because QString has a non-trivial ctor.
+ constexpr QJSPrimitiveValuePrivate() noexcept {}
+
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveUndefined) noexcept {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveNull) noexcept
+ : m_type(Null) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(bool b) noexcept
+ : m_bool(b), m_type(Boolean) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(int i) noexcept
+ : m_int(i), m_type(Integer) {}
+ Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(double d) noexcept
+ : m_double(d), m_type(Double) {}
+ Q_IMPLICIT QJSPrimitiveValuePrivate(QString s) noexcept
+ : m_string(std::move(s)), m_type(String) {}
+
+ constexpr QJSPrimitiveValuePrivate(const QJSPrimitiveValuePrivate &other) noexcept
+ : m_type(other.m_type)
+ {
+ // Not copy-and-swap since swap() would be much more complicated.
+ if (!assignSimple(other))
+ new (&m_string) QString(other.m_string);
+ }
+
+ constexpr QJSPrimitiveValuePrivate(QJSPrimitiveValuePrivate &&other) noexcept
+ : m_type(other.m_type)
+ {
+ // Not move-and-swap since swap() would be much more complicated.
+ if (!assignSimple(other))
+ new (&m_string) QString(std::move(other.m_string));
+ }
+
+ constexpr QJSPrimitiveValuePrivate &operator=(const QJSPrimitiveValuePrivate &other) noexcept
+ {
+ if (this == &other)
+ return *this;
+
+ if (m_type == String) {
+ if (other.m_type == String) {
+ m_type = other.m_type;
+ m_string = other.m_string;
+ return *this;
+ }
+ m_string.~QString();
+ }
+
+ m_type = other.m_type;
+ if (!assignSimple(other))
+ new (&m_string) QString(other.m_string);
+ return *this;
+ }
+
+ constexpr QJSPrimitiveValuePrivate &operator=(QJSPrimitiveValuePrivate &&other) noexcept
+ {
+ if (this == &other)
+ return *this;
+
+ if (m_type == String) {
+ if (other.m_type == String) {
+ m_type = other.m_type;
+ m_string = std::move(other.m_string);
+ return *this;
+ }
+ m_string.~QString();
+ }
+
+ m_type = other.m_type;
+ if (!assignSimple(other))
+ new (&m_string) QString(std::move(other.m_string));
+ return *this;
+ }
+
+ ~QJSPrimitiveValuePrivate()
+ {
+ if (m_type == String)
+ m_string.~QString();
+ }
+
+ constexpr Type type() const noexcept { return m_type; }
+ constexpr bool getBool() const noexcept { return m_bool; }
+ constexpr int getInt() const noexcept { return m_int; }
+ constexpr double getDouble() const noexcept { return m_double; }
+ QString getString() const noexcept { return m_string; }
+
+ template<typename T>
+ constexpr T get() const noexcept {
+ if constexpr (std::is_same_v<T, QJSPrimitiveUndefined>)
+ return QJSPrimitiveUndefined();
+ else if constexpr (std::is_same_v<T, QJSPrimitiveNull>)
+ return QJSPrimitiveNull();
+ else if constexpr (std::is_same_v<T, bool>)
+ return getBool();
+ else if constexpr (std::is_same_v<T, int>)
+ return getInt();
+ else if constexpr (std::is_same_v<T, double>)
+ return getDouble();
+ else if constexpr (std::is_same_v<T, QString>)
+ return getString();
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(T());
+ #else
+ return T();
+ #endif
+ }
+
+ constexpr QMetaType metaType() const noexcept {
+ switch (m_type) {
+ case Undefined:
+ return QMetaType();
+ case Null:
+ return QMetaType::fromType<std::nullptr_t>();
+ case Boolean:
+ return QMetaType::fromType<bool>();
+ case Integer:
+ return QMetaType::fromType<int>();
+ case Double:
+ return QMetaType::fromType<double>();
+ case String:
+ return QMetaType::fromType<QString>();
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(QMetaType());
+ #else
+ return QMetaType();
+ #endif
+ }
+
+ constexpr void *data() noexcept {
+ switch (m_type) {
+ case Undefined:
+ case Null:
+ return nullptr;
+ case Boolean:
+ return &m_bool;
+ case Integer:
+ return &m_int;
+ case Double:
+ return &m_double;
+ case String:
+ return &m_string;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(nullptr);
+ #else
+ return nullptr;
+ #endif
+ }
+
+ constexpr const void *data() const noexcept {
+ switch (m_type) {
+ case Undefined:
+ case Null:
+ return nullptr;
+ case Boolean:
+ return &m_bool;
+ case Integer:
+ return &m_int;
+ case Double:
+ return &m_double;
+ case String:
+ return &m_string;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(nullptr);
+ #else
+ return nullptr;
+ #endif
+ }
+
+ private:
+ constexpr bool assignSimple(const QJSPrimitiveValuePrivate &other) noexcept
+ {
+ switch (other.m_type) {
+ case Undefined:
+ case Null:
+ return true;
+ case Boolean:
+ m_bool = other.m_bool;
+ return true;
+ case Integer:
+ m_int = other.m_int;
+ return true;
+ case Double:
+ m_double = other.m_double;
+ return true;
+ case String:
+ return false;
+ }
+
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+ #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ Q_UNREACHABLE_RETURN(false);
+ #else
+ return false;
+ #endif
+ }
+
+ union {
+ bool m_bool = false;
+ int m_int;
+ double m_double;
+ QString m_string;
+ };
+
+ Type m_type = Undefined;
+ };
+
+ QJSPrimitiveValuePrivate d;
+};
+
+namespace QQmlPrivate {
+ // TODO: Make this constexpr once std::isnan is constexpr.
+ inline double jsExponentiate(double base, double exponent)
+ {
+ constexpr double qNaN = std::numeric_limits<double>::quiet_NaN();
+ constexpr double inf = std::numeric_limits<double>::infinity();
+
+ if (qIsNull(exponent))
+ return 1.0;
+
+ if (std::isnan(exponent))
+ return qNaN;
+
+ if (QJSNumberCoercion::equals(base, 1.0) || QJSNumberCoercion::equals(base, -1.0))
+ return std::isinf(exponent) ? qNaN : std::pow(base, exponent);
+
+ if (!qIsNull(base))
+ return std::pow(base, exponent);
+
+ if (std::copysign(1.0, base) > 0.0)
+ return exponent < 0.0 ? inf : std::pow(base, exponent);
+
+ if (exponent < 0.0)
+ return QJSNumberCoercion::equals(std::fmod(-exponent, 2.0), 1.0) ? -inf : inf;
+
+ return QJSNumberCoercion::equals(std::fmod(exponent, 2.0), 1.0)
+ ? std::copysign(0, -1.0)
+ : 0.0;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QJSPRIMITIVEVALUE_H