// 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 #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QV4 { struct ExecutionEngine; } struct QJSPrimitiveUndefined {}; struct QJSPrimitiveNull {}; class QJSPrimitiveValue { template struct StringNaNOperators { static constexpr double op(const QString &, QJSPrimitiveUndefined) { return std::numeric_limits::quiet_NaN(); } static constexpr double op(QJSPrimitiveUndefined, const QString &) { return std::numeric_limits::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 static double op(const QString &lhs, T rhs) { return Concrete::op(fromString(lhs).toDouble(), rhs); } template 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 static QString op(const QString &lhs, T rhs) { return lhs + QJSPrimitiveValue(rhs).toString(); } template 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 { 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 { 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 { 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(value); break; case QMetaType::Int: d = *static_cast(value); break; case QMetaType::Double: d = *static_cast(value); break; case QMetaType::QString: d = *static_cast(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 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::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(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(lhs, rhs); } friend inline QJSPrimitiveValue operator-(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) { return operate(lhs, rhs); } friend inline QJSPrimitiveValue operator*(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) { return operate(lhs, rhs); } friend inline QJSPrimitiveValue operator/(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) { return operate(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(); } default: break; } Q_FALLTHROUGH(); default: 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(*this, -1)); } QJSPrimitiveValue operator++(int) { // a++ is modeled as a -= (-1) to avoid the potential string concatenation QJSPrimitiveValue other = operate(*this, -1); std::swap(other, *this); return +other; // We still need to coerce the original value. } QJSPrimitiveValue &operator--() { return (*this = operate(*this, 1)); } QJSPrimitiveValue operator--(int) { QJSPrimitiveValue other = operate(*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(*this, 0)); } QJSPrimitiveValue operator-() { return (*this = operate(*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::infinity(); if (string == QStringLiteral("-Infinity")) return -std::numeric_limits::infinity(); if (string == QStringLiteral("NaN")) return std::numeric_limits::quiet_NaN(); return QJSPrimitiveUndefined(); } static Q_QML_EXPORT QString toString(double d); template static QJSPrimitiveValue operateOnIntegers(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) { int result; if (Operators::opOverflow(lhs.d.get(), rhs.d.get(), &result)) return Operators::op(lhs.d.get(), rhs.d.get()); return result; } template static QJSPrimitiveValue operate(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs) { switch (lhs.type()) { case Undefined: switch (rhs.type()) { case Undefined: return std::numeric_limits::quiet_NaN(); case Null: return std::numeric_limits::quiet_NaN(); case Boolean: return std::numeric_limits::quiet_NaN(); case Integer: return std::numeric_limits::quiet_NaN(); case Double: return std::numeric_limits::quiet_NaN(); case String: return Operators::op(QJSPrimitiveUndefined(), rhs.asString()); } break; case Null: switch (rhs.type()) { case Undefined: return std::numeric_limits::quiet_NaN(); case Null: return operateOnIntegers(0, 0); case Boolean: return operateOnIntegers(0, rhs); case Integer: return operateOnIntegers(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::quiet_NaN(); case Null: return operateOnIntegers(lhs, 0); case Boolean: return operateOnIntegers(lhs, rhs); case Integer: return operateOnIntegers(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::quiet_NaN(); case Null: return operateOnIntegers(lhs, 0); case Boolean: return operateOnIntegers(lhs, rhs); case Integer: return operateOnIntegers(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::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; } } 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 constexpr T get() const noexcept { if constexpr (std::is_same_v) return QJSPrimitiveUndefined(); else if constexpr (std::is_same_v) return QJSPrimitiveNull(); else if constexpr (std::is_same_v) return getBool(); else if constexpr (std::is_same_v) return getInt(); else if constexpr (std::is_same_v) return getDouble(); else if constexpr (std::is_same_v) 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(); case Boolean: return QMetaType::fromType(); case Integer: return QMetaType::fromType(); case Double: return QMetaType::fromType(); case String: return QMetaType::fromType(); } // 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::quiet_NaN(); constexpr double inf = std::numeric_limits::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