Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qjsprimitivevalue.h
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5#ifndef QJSPRIMITIVEVALUE_H
6#define QJSPRIMITIVEVALUE_H
7
8#include <QtQml/qtqmlglobal.h>
9#include <QtQml/qjsnumbercoercion.h>
10
11#include <QtCore/qstring.h>
12#include <QtCore/qnumeric.h>
13#include <QtCore/qvariant.h>
14
15#include <variant>
16#include <cmath>
17
18QT_BEGIN_NAMESPACE
19
20namespace QV4 { struct ExecutionEngine; }
21
24
26{
27 template<typename Concrete>
28 struct StringNaNOperators
29 {
30 static constexpr double op(const QString &, QJSPrimitiveUndefined)
31 {
32 return std::numeric_limits<double>::quiet_NaN();
33 }
34
35 static constexpr double op(QJSPrimitiveUndefined, const QString &)
36 {
37 return std::numeric_limits<double>::quiet_NaN();
38 }
39
40 static double op(const QString &lhs, QJSPrimitiveNull) { return op(lhs, 0); }
41 static double op(QJSPrimitiveNull, const QString &rhs) { return op(0, rhs); }
42
43 template<typename T>
44 static double op(const QString &lhs, T rhs)
45 {
46 return Concrete::op(numberFromString(lhs).toDouble(), rhs);
47 }
48
49 template<typename T>
50 static double op(T lhs, const QString &rhs)
51 {
52 return Concrete::op(lhs, numberFromString(rhs).toDouble());
53 }
54
55 static double op(const QString &lhs, const QString &rhs)
56 {
57 return Concrete::op(numberFromString(lhs).toDouble(), numberFromString(rhs).toDouble());
58 }
59 };
60
61 struct AddOperators {
62 static constexpr double op(double lhs, double rhs) { return lhs + rhs; }
63 static bool opOverflow(int lhs, int rhs, int *result)
64 {
65 return qAddOverflow(lhs, rhs, result);
66 }
67
68 template<typename T>
69 static QString op(const QString &lhs, T rhs)
70 {
71 return lhs + QJSPrimitiveValue(rhs).toString();
72 }
73
74 template<typename T>
75 static QString op(T lhs, const QString &rhs)
76 {
77 return QJSPrimitiveValue(lhs).toString() + rhs;
78 }
79
80 static QString op(const QString &lhs, const QString &rhs) { return lhs + rhs; }
81 };
82
83 struct SubOperators : private StringNaNOperators<SubOperators> {
84 static constexpr double op(double lhs, double rhs) { return lhs - rhs; }
85 static bool opOverflow(int lhs, int rhs, int *result)
86 {
87 return qSubOverflow(lhs, rhs, result);
88 }
89
90 using StringNaNOperators::op;
91 };
92
93 struct MulOperators : private StringNaNOperators<MulOperators> {
94 static constexpr double op(double lhs, double rhs) { return lhs * rhs; }
95 static bool opOverflow(int lhs, int rhs, int *result)
96 {
97 // compare mul_int32 in qv4math_p.h
98 auto hadOverflow = qMulOverflow(lhs, rhs, result);
99 if (((lhs < 0) ^ (rhs < 0)) && (*result == 0))
100 return true; // result must be negative 0, does not fit into int
101 return hadOverflow;
102 }
103
104 using StringNaNOperators::op;
105 };
106
107 struct DivOperators : private StringNaNOperators<DivOperators> {
108 static constexpr double op(double lhs, double rhs) { return lhs / rhs; }
109 static constexpr bool opOverflow(int, int, int *)
110 {
111 return true;
112 }
113
114 using StringNaNOperators::op;
115 };
116
117public:
126
127 constexpr Type type() const { return Type(d.type()); }
128
129 // Prevent casting from Type to int
131
132 Q_IMPLICIT constexpr QJSPrimitiveValue() noexcept = default;
135 Q_IMPLICIT constexpr QJSPrimitiveValue(bool value) noexcept : d(value) {}
136 Q_IMPLICIT constexpr QJSPrimitiveValue(int value) noexcept : d(value) {}
137 Q_IMPLICIT constexpr QJSPrimitiveValue(double value) noexcept : d(value) {}
139
140 explicit QJSPrimitiveValue(const QMetaType type, const void *value) noexcept
141 {
142 switch (type.id()) {
143 case QMetaType::UnknownType:
145 break;
146 case QMetaType::Nullptr:
147 d = QJSPrimitiveNull();
148 break;
149 case QMetaType::Bool:
150 d = *static_cast<const bool *>(value);
151 break;
152 case QMetaType::Int:
153 d = *static_cast<const int *>(value);
154 break;
155 case QMetaType::Double:
156 d = *static_cast<const double *>(value);
157 break;
158 case QMetaType::QString:
159 d = *static_cast<const QString *>(value);
160 break;
161 default:
162 // Unsupported. Remains undefined.
163 break;
164 }
165 }
166
167 explicit QJSPrimitiveValue(QMetaType type) noexcept
168 {
169 switch (type.id()) {
170 case QMetaType::UnknownType:
172 break;
173 case QMetaType::Nullptr:
174 d = QJSPrimitiveNull();
175 break;
176 case QMetaType::Bool:
177 d = false;
178 break;
179 case QMetaType::Int:
180 d = 0;
181 break;
182 case QMetaType::Double:
183 d = 0.0;
184 break;
185 case QMetaType::QString:
186 d = QString();
187 break;
188 default:
189 // Unsupported. Remains undefined.
190 break;
191 }
192 }
193
194 explicit QJSPrimitiveValue(const QVariant &variant) noexcept
195 : QJSPrimitiveValue(variant.metaType(), variant.data())
196 {
197 }
198
199 constexpr QMetaType metaType() const { return d.metaType(); }
200 constexpr void *data() { return d.data(); }
201 constexpr const void *data() const { return d.data(); }
202 constexpr const void *constData() const { return d.data(); }
203
204 template<Type type>
206 if constexpr (type == Undefined)
207 return QJSPrimitiveUndefined();
208 if constexpr (type == Null)
209 return QJSPrimitiveNull();
210 if constexpr (type == Boolean)
211 return toBoolean();
212 if constexpr (type == Integer)
213 return toInteger();
214 if constexpr (type == Double)
215 return toDouble();
216 if constexpr (type == String)
217 return toString();
218
219 Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
220 }
221
222 constexpr bool toBoolean() const
223 {
224 switch (type()) {
225 case Undefined: return false;
226 case Null: return false;
227 case Boolean: return asBoolean();
228 case Integer: return asInteger() != 0;
229 case Double: {
230 const double v = asDouble();
231 return !QJSNumberCoercion::equals(v, 0) && !std::isnan(v);
232 }
233 case String: return !asString().isEmpty();
234 }
235
236 // GCC 8.x does not treat __builtin_unreachable() as constexpr
237 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
238 Q_UNREACHABLE_RETURN(false);
239 #else
240 return false;
241 #endif
242 }
243
244 constexpr int toInteger() const
245 {
246 switch (type()) {
247 case Undefined: return 0;
248 case Null: return 0;
249 case Boolean: return asBoolean();
250 case Integer: return asInteger();
251 case Double: return QJSNumberCoercion::toInteger(asDouble());
252 case String: return numberFromString(asString()).toInteger();
253 }
254
255 // GCC 8.x does not treat __builtin_unreachable() as constexpr
256 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
257 Q_UNREACHABLE_RETURN(0);
258 #else
259 return 0;
260 #endif
261 }
262
263 constexpr double toDouble() const
264 {
265 switch (type()) {
266 case Undefined: return std::numeric_limits<double>::quiet_NaN();
267 case Null: return 0;
268 case Boolean: return asBoolean();
269 case Integer: return asInteger();
270 case Double: return asDouble();
271 case String: return numberFromString(asString()).toDouble();
272 }
273
274 // GCC 8.x does not treat __builtin_unreachable() as constexpr
275 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
276 Q_UNREACHABLE_RETURN({});
277 #else
278 return {};
279 #endif
280 }
281
283 {
284 switch (type()) {
285 case Undefined: return QStringLiteral("undefined");
286 case Null: return QStringLiteral("null");
287 case Boolean: return asBoolean() ? QStringLiteral("true") : QStringLiteral("false");
288 case Integer: return QString::number(asInteger());
289 case Double: {
290 const double result = asDouble();
291 if (std::isnan(result))
292 return QStringLiteral("NaN");
293 if (std::isfinite(result))
294 return toString(result);
295 if (result > 0)
296 return QStringLiteral("Infinity");
297 return QStringLiteral("-Infinity");
298 }
299 case String: return asString();
300 }
301
302 Q_UNREACHABLE_RETURN(QString());
303 }
304
306 {
307 switch (type()) {
308 case Undefined: return QVariant();
309 case Null: return QVariant::fromValue<std::nullptr_t>(nullptr);
310 case Boolean: return QVariant(asBoolean());
311 case Integer: return QVariant(asInteger());
312 case Double: return QVariant(asDouble());
313 case String: return QVariant(asString());
314 }
315
316 Q_UNREACHABLE_RETURN(QVariant());
317 }
318
320 const QJSPrimitiveValue &rhs)
321 {
322 return operate<AddOperators>(lhs, rhs);
323 }
324
326 const QJSPrimitiveValue &rhs)
327 {
328 return operate<SubOperators>(lhs, rhs);
329 }
330
332 const QJSPrimitiveValue &rhs)
333 {
334 return operate<MulOperators>(lhs, rhs);
335 }
336
338 const QJSPrimitiveValue &rhs)
339 {
340 return operate<DivOperators>(lhs, rhs);
341 }
342
344 const QJSPrimitiveValue &rhs)
345 {
346 switch (lhs.type()) {
347 case Null:
348 case Boolean:
349 case Integer:
350 switch (rhs.type()) {
351 case Boolean:
352 case Integer: {
353 const int leftInt = lhs.toInteger();
354 const int rightInt = rhs.toInteger();
355 if (leftInt >= 0 && rightInt > 0)
356 return leftInt % rightInt;
357 Q_FALLTHROUGH();
358 }
359 case Undefined:
360 case Null:
361 case Double:
362 case String:
363 break;
364 }
365 Q_FALLTHROUGH();
366 case Undefined:
367 case Double:
368 case String:
369 break;
370 }
371
372 return std::fmod(lhs.toDouble(), rhs.toDouble());
373 }
374
376 {
377 // ++a is modeled as a -= (-1) to avoid the potential string concatenation
378 return (*this = operate<SubOperators>(*this, -1));
379 }
380
382 {
383 // a++ is modeled as a -= (-1) to avoid the potential string concatenation
384 QJSPrimitiveValue other = operate<SubOperators>(*this, -1);
385 std::swap(other, *this);
386 return +other; // We still need to coerce the original value.
387 }
388
390 {
391 return (*this = operate<SubOperators>(*this, 1));
392 }
393
395 {
396 QJSPrimitiveValue other = operate<SubOperators>(*this, 1);
397 std::swap(other, *this);
398 return +other; // We still need to coerce the original value.
399 }
400
402 {
403 // +a is modeled as a -= 0. That should force it to number.
404 return (*this = operate<SubOperators>(*this, 0));
405 }
406
408 {
409 return (*this = operate<MulOperators>(*this, -1));
410 }
411
412 constexpr bool strictlyEquals(const QJSPrimitiveValue &other) const
413 {
414 const Type myType = type();
415 const Type otherType = other.type();
416
417 if (myType != otherType) {
418 // int -> double promotion is OK in strict mode
419 if (myType == Double && otherType == Integer)
420 return strictlyEquals(double(other.asInteger()));
421 if (myType == Integer && otherType == Double)
422 return QJSPrimitiveValue(double(asInteger())).strictlyEquals(other);
423 return false;
424 }
425
426 switch (myType) {
427 case Undefined:
428 case Null:
429 return true;
430 case Boolean:
431 return asBoolean() == other.asBoolean();
432 case Integer:
433 return asInteger() == other.asInteger();
434 case Double: {
435 const double l = asDouble();
436 const double r = other.asDouble();
437 if (std::isnan(l) || std::isnan(r))
438 return false;
439 if (qIsNull(l) && qIsNull(r))
440 return true;
441 return QJSNumberCoercion::equals(l, r);
442 }
443 case String:
444 return asString() == other.asString();
445 }
446
447 return false;
448 }
449
450 // Loose operator==, in contrast to strict ===
451 constexpr bool equals(const QJSPrimitiveValue &other) const
452 {
453 const Type myType = type();
454 const Type otherType = other.type();
455
456 if (myType == otherType)
457 return strictlyEquals(other);
458
459 switch (myType) {
460 case Undefined:
461 return otherType == Null;
462 case Null:
463 return otherType == Undefined;
464 case Boolean:
465 return QJSPrimitiveValue(int(asBoolean())).equals(other);
466 case Integer:
467 // prefer rhs bool -> int promotion over promoting both to double
468 return otherType == Boolean
469 ? QJSPrimitiveValue(asInteger()).equals(int(other.asBoolean()))
470 : QJSPrimitiveValue(double(asInteger())).equals(other);
471 case Double:
472 // Promote the other side to double (or recognize lhs as undefined/null)
473 return other.equals(*this);
474 case String:
475 return numberFromString(asString()).parsedEquals(other);
476 }
477
478 return false;
479 }
480
481 friend constexpr inline bool operator==(const QJSPrimitiveValue &lhs, const
483 {
484 return lhs.strictlyEquals(rhs);
485 }
486
487 friend constexpr inline bool operator!=(const QJSPrimitiveValue &lhs,
488 const QJSPrimitiveValue &rhs)
489 {
490 return !lhs.strictlyEquals(rhs);
491 }
492
493 friend constexpr inline bool operator<(const QJSPrimitiveValue &lhs,
494 const QJSPrimitiveValue &rhs)
495 {
496 switch (lhs.type()) {
497 case Undefined:
498 return false;
499 case Null: {
500 switch (rhs.type()) {
501 case Undefined: return false;
502 case Null: return false;
503 case Boolean: return 0 < int(rhs.asBoolean());
504 case Integer: return 0 < rhs.asInteger();
505 case Double: return double(0) < rhs.asDouble();
506 case String: return double(0) < rhs.toDouble();
507 }
508 break;
509 }
510 case Boolean: {
511 switch (rhs.type()) {
512 case Undefined: return false;
513 case Null: return int(lhs.asBoolean()) < 0;
514 case Boolean: return lhs.asBoolean() < rhs.asBoolean();
515 case Integer: return int(lhs.asBoolean()) < rhs.asInteger();
516 case Double: return double(lhs.asBoolean()) < rhs.asDouble();
517 case String: return double(lhs.asBoolean()) < rhs.toDouble();
518 }
519 break;
520 }
521 case Integer: {
522 switch (rhs.type()) {
523 case Undefined: return false;
524 case Null: return lhs.asInteger() < 0;
525 case Boolean: return lhs.asInteger() < int(rhs.asBoolean());
526 case Integer: return lhs.asInteger() < rhs.asInteger();
527 case Double: return double(lhs.asInteger()) < rhs.asDouble();
528 case String: return double(lhs.asInteger()) < rhs.toDouble();
529 }
530 break;
531 }
532 case Double: {
533 switch (rhs.type()) {
534 case Undefined: return false;
535 case Null: return lhs.asDouble() < double(0);
536 case Boolean: return lhs.asDouble() < double(rhs.asBoolean());
537 case Integer: return lhs.asDouble() < double(rhs.asInteger());
538 case Double: return lhs.asDouble() < rhs.asDouble();
539 case String: return lhs.asDouble() < rhs.toDouble();
540 }
541 break;
542 }
543 case String: {
544 switch (rhs.type()) {
545 case Undefined: return false;
546 case Null: return lhs.toDouble() < double(0);
547 case Boolean: return lhs.toDouble() < double(rhs.asBoolean());
548 case Integer: return lhs.toDouble() < double(rhs.asInteger());
549 case Double: return lhs.toDouble() < rhs.asDouble();
550 case String: return lhs.asString() < rhs.asString();
551 }
552 break;
553 }
554 }
555
556 return false;
557 }
558
559 friend constexpr inline bool operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
560 {
561 return rhs < lhs;
562 }
563
564 friend constexpr inline bool operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
565 {
566 if (lhs.type() == String) {
567 if (rhs.type() == String)
568 return lhs.asString() <= rhs.asString();
569 else
570 return numberFromString(lhs.asString()) <= rhs;
571 }
572 if (rhs.type() == String)
573 return lhs <= numberFromString(rhs.asString());
574
575 if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined())
576 return false;
577 return !(lhs > rhs);
578 }
579
580 friend constexpr inline bool operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
581 {
582 if (lhs.type() == String) {
583 if (rhs.type() == String)
584 return lhs.asString() >= rhs.asString();
585 else
586 return numberFromString(lhs.asString()) >= rhs;
587 }
588 if (rhs.type() == String)
589 return lhs >= numberFromString(rhs.asString());
590
591 if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined())
592 return false;
593 return !(lhs < rhs);
594 }
595
596private:
597 friend class QJSManagedValue;
598 friend class QJSValue;
599 friend struct QV4::ExecutionEngine;
600
601 constexpr bool asBoolean() const { return d.getBool(); }
602 constexpr int asInteger() const { return d.getInt(); }
603 constexpr double asDouble() const { return d.getDouble(); }
604 QString asString() const { return d.getString(); }
605
606 constexpr bool parsedEquals(const QJSPrimitiveValue &other) const
607 {
608 return type() != Undefined && equals(other);
609 }
610
611 static QJSPrimitiveValue numberFromString(const QString &string)
612 {
613 bool ok;
614 const int intValue = string.toInt(&ok);
615 if (ok)
616 return intValue;
617
618 const double doubleValue = string.toDouble(&ok);
619 if (ok)
620 return doubleValue;
621 if (string.isEmpty())
622 return 0;
623 if (string == QStringLiteral("Infinity"))
624 return std::numeric_limits<double>::infinity();
625 if (string == QStringLiteral("-Infinity"))
626 return -std::numeric_limits<double>::infinity();
627 if (string == QStringLiteral("NaN"))
628 return std::numeric_limits<double>::quiet_NaN();
629 return QJSPrimitiveUndefined();
630 }
631
632 static Q_QML_EXPORT QString toString(double d);
633
634 template<typename Operators, typename Lhs, typename Rhs>
635 static QJSPrimitiveValue operateOnIntegers(const QJSPrimitiveValue &lhs,
636 const QJSPrimitiveValue &rhs)
637 {
638 int result;
639 if (Operators::opOverflow(lhs.d.get<Lhs>(), rhs.d.get<Rhs>(), &result))
640 return Operators::op(lhs.d.get<Lhs>(), rhs.d.get<Rhs>());
641 return result;
642 }
643
644 template<typename Operators>
645 static QJSPrimitiveValue operate(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
646 {
647 switch (lhs.type()) {
648 case Undefined:
649 switch (rhs.type()) {
650 case Undefined: return std::numeric_limits<double>::quiet_NaN();
651 case Null: return std::numeric_limits<double>::quiet_NaN();
652 case Boolean: return std::numeric_limits<double>::quiet_NaN();
653 case Integer: return std::numeric_limits<double>::quiet_NaN();
654 case Double: return std::numeric_limits<double>::quiet_NaN();
655 case String: return Operators::op(QJSPrimitiveUndefined(), rhs.asString());
656 }
657 break;
658 case Null:
659 switch (rhs.type()) {
660 case Undefined: return std::numeric_limits<double>::quiet_NaN();
661 case Null: return operateOnIntegers<Operators, int, int>(0, 0);
662 case Boolean: return operateOnIntegers<Operators, int, bool>(0, rhs);
663 case Integer: return operateOnIntegers<Operators, int, int>(0, rhs);
664 case Double: return Operators::op(0, rhs.asDouble());
665 case String: return Operators::op(QJSPrimitiveNull(), rhs.asString());
666 }
667 break;
668 case Boolean:
669 switch (rhs.type()) {
670 case Undefined: return std::numeric_limits<double>::quiet_NaN();
671 case Null: return operateOnIntegers<Operators, bool, int>(lhs, 0);
672 case Boolean: return operateOnIntegers<Operators, bool, bool>(lhs, rhs);
673 case Integer: return operateOnIntegers<Operators, bool, int>(lhs, rhs);
674 case Double: return Operators::op(lhs.asBoolean(), rhs.asDouble());
675 case String: return Operators::op(lhs.asBoolean(), rhs.asString());
676 }
677 break;
678 case Integer:
679 switch (rhs.type()) {
680 case Undefined: return std::numeric_limits<double>::quiet_NaN();
681 case Null: return operateOnIntegers<Operators, int, int>(lhs, 0);
682 case Boolean: return operateOnIntegers<Operators, int, bool>(lhs, rhs);
683 case Integer: return operateOnIntegers<Operators, int, int>(lhs, rhs);
684 case Double: return Operators::op(lhs.asInteger(), rhs.asDouble());
685 case String: return Operators::op(lhs.asInteger(), rhs.asString());
686 }
687 break;
688 case Double:
689 switch (rhs.type()) {
690 case Undefined: return std::numeric_limits<double>::quiet_NaN();
691 case Null: return Operators::op(lhs.asDouble(), 0);
692 case Boolean: return Operators::op(lhs.asDouble(), rhs.asBoolean());
693 case Integer: return Operators::op(lhs.asDouble(), rhs.asInteger());
694 case Double: return Operators::op(lhs.asDouble(), rhs.asDouble());
695 case String: return Operators::op(lhs.asDouble(), rhs.asString());
696 }
697 break;
698 case String:
699 switch (rhs.type()) {
700 case Undefined: return Operators::op(lhs.asString(), QJSPrimitiveUndefined());
701 case Null: return Operators::op(lhs.asString(), QJSPrimitiveNull());
702 case Boolean: return Operators::op(lhs.asString(), rhs.asBoolean());
703 case Integer: return Operators::op(lhs.asString(), rhs.asInteger());
704 case Double: return Operators::op(lhs.asString(), rhs.asDouble());
705 case String: return Operators::op(lhs.asString(), rhs.asString());
706 }
707 break;
708 }
709
710 Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
711 }
712
713 constexpr bool isNanOrUndefined() const
714 {
715 switch (type()) {
716 case Undefined: return true;
717 case Null: return false;
718 case Boolean: return false;
719 case Integer: return false;
720 case Double: return std::isnan(asDouble());
721 case String: return false;
722 }
723 // GCC 8.x does not treat __builtin_unreachable() as constexpr
724 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
725 Q_UNREACHABLE_RETURN(false);
726 #else
727 return false;
728 #endif
729 }
730
731 struct QJSPrimitiveValuePrivate
732 {
733 // Can't be default because QString has a non-trivial ctor.
734 constexpr QJSPrimitiveValuePrivate() noexcept {}
735
736 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveUndefined) noexcept {}
737 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveNull) noexcept
738 : m_type(Null) {}
739 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(bool b) noexcept
740 : m_bool(b), m_type(Boolean) {}
741 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(int i) noexcept
742 : m_int(i), m_type(Integer) {}
743 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(double d) noexcept
744 : m_double(d), m_type(Double) {}
745 Q_IMPLICIT QJSPrimitiveValuePrivate(QString s) noexcept
746 : m_string(std::move(s)), m_type(String) {}
747
748 constexpr QJSPrimitiveValuePrivate(const QJSPrimitiveValuePrivate &other) noexcept
749 : m_type(other.m_type)
750 {
751 // Not copy-and-swap since swap() would be much more complicated.
752 if (!assignSimple(other))
753 new (&m_string) QString(other.m_string);
754 }
755
756 constexpr QJSPrimitiveValuePrivate(QJSPrimitiveValuePrivate &&other) noexcept
757 : m_type(other.m_type)
758 {
759 // Not move-and-swap since swap() would be much more complicated.
760 if (!assignSimple(other))
761 new (&m_string) QString(std::move(other.m_string));
762 }
763
764 constexpr QJSPrimitiveValuePrivate &operator=(const QJSPrimitiveValuePrivate &other) noexcept
765 {
766 if (this == &other)
767 return *this;
768
769 if (m_type == String) {
770 if (other.m_type == String) {
771 m_type = other.m_type;
772 m_string = other.m_string;
773 return *this;
774 }
775 m_string.~QString();
776 }
777
778 m_type = other.m_type;
779 if (!assignSimple(other))
780 new (&m_string) QString(other.m_string);
781 return *this;
782 }
783
784 constexpr QJSPrimitiveValuePrivate &operator=(QJSPrimitiveValuePrivate &&other) noexcept
785 {
786 if (this == &other)
787 return *this;
788
789 if (m_type == String) {
790 if (other.m_type == String) {
791 m_type = other.m_type;
792 m_string = std::move(other.m_string);
793 return *this;
794 }
795 m_string.~QString();
796 }
797
798 m_type = other.m_type;
799 if (!assignSimple(other))
800 new (&m_string) QString(std::move(other.m_string));
801 return *this;
802 }
803
804 ~QJSPrimitiveValuePrivate()
805 {
806 if (m_type == String)
807 m_string.~QString();
808 }
809
810 constexpr Type type() const noexcept { return m_type; }
811 constexpr bool getBool() const noexcept { return m_bool; }
812 constexpr int getInt() const noexcept { return m_int; }
813 constexpr double getDouble() const noexcept { return m_double; }
814 QString getString() const noexcept { return m_string; }
815
816 template<typename T>
817 constexpr T get() const noexcept {
818 if constexpr (std::is_same_v<T, QJSPrimitiveUndefined>)
819 return QJSPrimitiveUndefined();
820 else if constexpr (std::is_same_v<T, QJSPrimitiveNull>)
821 return QJSPrimitiveNull();
822 else if constexpr (std::is_same_v<T, bool>)
823 return getBool();
824 else if constexpr (std::is_same_v<T, int>)
825 return getInt();
826 else if constexpr (std::is_same_v<T, double>)
827 return getDouble();
828 else if constexpr (std::is_same_v<T, QString>)
829 return getString();
830
831 // GCC 8.x does not treat __builtin_unreachable() as constexpr
832 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
833 Q_UNREACHABLE_RETURN(T());
834 #else
835 return T();
836 #endif
837 }
838
839 constexpr QMetaType metaType() const noexcept {
840 switch (m_type) {
841 case Undefined:
842 return QMetaType();
843 case Null:
844 return QMetaType::fromType<std::nullptr_t>();
845 case Boolean:
846 return QMetaType::fromType<bool>();
847 case Integer:
848 return QMetaType::fromType<int>();
849 case Double:
850 return QMetaType::fromType<double>();
851 case String:
852 return QMetaType::fromType<QString>();
853 }
854
855 // GCC 8.x does not treat __builtin_unreachable() as constexpr
856 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
857 Q_UNREACHABLE_RETURN(QMetaType());
858 #else
859 return QMetaType();
860 #endif
861 }
862
863 constexpr void *data() noexcept {
864 switch (m_type) {
865 case Undefined:
866 case Null:
867 return nullptr;
868 case Boolean:
869 return &m_bool;
870 case Integer:
871 return &m_int;
872 case Double:
873 return &m_double;
874 case String:
875 return &m_string;
876 }
877
878 // GCC 8.x does not treat __builtin_unreachable() as constexpr
879 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
880 Q_UNREACHABLE_RETURN(nullptr);
881 #else
882 return nullptr;
883 #endif
884 }
885
886 constexpr const void *data() const noexcept {
887 switch (m_type) {
888 case Undefined:
889 case Null:
890 return nullptr;
891 case Boolean:
892 return &m_bool;
893 case Integer:
894 return &m_int;
895 case Double:
896 return &m_double;
897 case String:
898 return &m_string;
899 }
900
901 // GCC 8.x does not treat __builtin_unreachable() as constexpr
902 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
903 Q_UNREACHABLE_RETURN(nullptr);
904 #else
905 return nullptr;
906 #endif
907 }
908
909 private:
910 constexpr bool assignSimple(const QJSPrimitiveValuePrivate &other) noexcept
911 {
912 switch (other.m_type) {
913 case Undefined:
914 case Null:
915 return true;
916 case Boolean:
917 m_bool = other.m_bool;
918 return true;
919 case Integer:
920 m_int = other.m_int;
921 return true;
922 case Double:
923 m_double = other.m_double;
924 return true;
925 case String:
926 return false;
927 }
928
929 // GCC 8.x does not treat __builtin_unreachable() as constexpr
930 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
931 Q_UNREACHABLE_RETURN(false);
932 #else
933 return false;
934 #endif
935 }
936
937 union {
938 bool m_bool;
939 int m_int;
940 double m_double;
941 QString m_string;
942
943 // Dummy value to trigger initialization of the whole storage.
944 // We don't want to see maybe-uninitialized warnings every time we access m_string.
945 std::byte m_zeroInitialize[sizeof(QString)] = {};
946 };
947
948 Type m_type = Undefined;
949 };
950
951 QJSPrimitiveValuePrivate d;
952};
953
954namespace QQmlPrivate {
955 // TODO: Make this constexpr once std::isnan is constexpr.
956 inline double jsExponentiate(double base, double exponent)
957 {
958 constexpr double qNaN = std::numeric_limits<double>::quiet_NaN();
959 constexpr double inf = std::numeric_limits<double>::infinity();
960
961 if (qIsNull(exponent))
962 return 1.0;
963
964 if (std::isnan(exponent))
965 return qNaN;
966
967 if (QJSNumberCoercion::equals(base, 1.0) || QJSNumberCoercion::equals(base, -1.0))
968 return std::isinf(exponent) ? qNaN : std::pow(base, exponent);
969
970 if (!qIsNull(base))
971 return std::pow(base, exponent);
972
973 if (std::copysign(1.0, base) > 0.0)
974 return exponent < 0.0 ? inf : std::pow(base, exponent);
975
976 if (exponent < 0.0)
977 return QJSNumberCoercion::equals(std::fmod(-exponent, 2.0), 1.0) ? -inf : inf;
978
979 return QJSNumberCoercion::equals(std::fmod(exponent, 2.0), 1.0)
980 ? std::copysign(0, -1.0)
981 : 0.0;
982 }
983}
984
985QT_END_NAMESPACE
986
987#endif // QJSPRIMITIVEVALUE_H
The QJSPrimitiveValue class operates on primitive types in JavaScript semantics.
constexpr const void * data() const
constexpr const void * constData() const
QJSPrimitiveValue(Type)=delete
friend constexpr bool operator<(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
constexpr int toInteger() const
Returns the value coerced to an integral 32bit number by the rules JavaScript would apply when prepar...
friend constexpr bool operator!=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
constexpr Type type() const
Returns the type of the QJSPrimitiveValue.
QJSPrimitiveValue(QMetaType type) noexcept
constexpr bool toBoolean() const
Returns the value coerced a boolean by JavaScript rules.
QJSPrimitiveValue operator++(int)
QJSPrimitiveValue operator+()
QJSPrimitiveValue to() const
QVariant toVariant() const
QJSPrimitiveValue(const QVariant &variant) noexcept
Creates a QJSPrimitiveValue from the contents of value if those contents can be stored in QJSPrimtive...
QJSPrimitiveValue operator--(int)
constexpr bool strictlyEquals(const QJSPrimitiveValue &other) const
Performs the JavaScript '===' operation on this QJSPrimitiveValue and other, and returns the result.
friend constexpr bool operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
friend QJSPrimitiveValue operator/(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
friend QJSPrimitiveValue operator+(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
friend constexpr bool operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
friend QJSPrimitiveValue operator*(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
Type
This enum speicifies the types a QJSPrimitiveValue might contain.
QJSPrimitiveValue & operator--()
constexpr QMetaType metaType() const
QJSPrimitiveValue(const QMetaType type, const void *value) noexcept
constexpr double toDouble() const
Returns the value coerced to a JavaScript Number by JavaScript rules.
QString toString() const
Returns the value coerced to a JavaScript String by JavaScript rules.
QJSPrimitiveValue & operator++()
friend constexpr bool operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
QJSPrimitiveValue operator-()
friend constexpr bool operator==(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
friend QJSPrimitiveValue operator%(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
constexpr void * data()
constexpr bool equals(const QJSPrimitiveValue &other) const
Performs the JavaScript '==' operation on this QJSPrimitiveValue and other, and returns the result.
friend QJSPrimitiveValue operator-(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
double jsExponentiate(double base, double exponent)
Combined button and popup list for selecting options.
Definition qjsvalue.h:24
An empty marker type to signify the JavaScript null value. \inmodule QtQml.
An empty marker type to signify the JavaScript Undefined type and its single value....