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) {
109 // Without is_iec559, we don't get proper JS semantics
110#ifndef Q_OS_INTEGRITY
111 static_assert(std::numeric_limits<double>::is_iec559);
112#endif
113 QT_WARNING_PUSH
114 // divide by zero: not an issue with iec559
115 QT_WARNING_DISABLE_MSVC(4723)
116 return lhs / rhs;
117 QT_WARNING_POP
118 }
119 static constexpr bool opOverflow(int, int, int *)
120 {
121 return true;
122 }
123
124 using StringNaNOperators::op;
125 };
126
127public:
136
137 constexpr Type type() const { return Type(d.type()); }
138
139 // Prevent casting from Type to int
141
142 Q_IMPLICIT constexpr QJSPrimitiveValue() noexcept = default;
145 Q_IMPLICIT constexpr QJSPrimitiveValue(bool value) noexcept : d(value) {}
146 Q_IMPLICIT constexpr QJSPrimitiveValue(int value) noexcept : d(value) {}
147 Q_IMPLICIT constexpr QJSPrimitiveValue(double value) noexcept : d(value) {}
149
150 explicit QJSPrimitiveValue(const QMetaType type, const void *value) noexcept
151 {
152 switch (type.id()) {
153 case QMetaType::UnknownType:
155 break;
156 case QMetaType::Nullptr:
157 d = QJSPrimitiveNull();
158 break;
159 case QMetaType::Bool:
160 d = *static_cast<const bool *>(value);
161 break;
162 case QMetaType::Int:
163 d = *static_cast<const int *>(value);
164 break;
165 case QMetaType::Double:
166 d = *static_cast<const double *>(value);
167 break;
168 case QMetaType::QString:
169 d = *static_cast<const QString *>(value);
170 break;
171 default:
172 // Unsupported. Remains undefined.
173 break;
174 }
175 }
176
177 explicit QJSPrimitiveValue(QMetaType type) noexcept
178 {
179 switch (type.id()) {
180 case QMetaType::UnknownType:
182 break;
183 case QMetaType::Nullptr:
184 d = QJSPrimitiveNull();
185 break;
186 case QMetaType::Bool:
187 d = false;
188 break;
189 case QMetaType::Int:
190 d = 0;
191 break;
192 case QMetaType::Double:
193 d = 0.0;
194 break;
195 case QMetaType::QString:
196 d = QString();
197 break;
198 default:
199 // Unsupported. Remains undefined.
200 break;
201 }
202 }
203
204 explicit QJSPrimitiveValue(const QVariant &variant) noexcept
205 : QJSPrimitiveValue(variant.metaType(), variant.data())
206 {
207 }
208
209 constexpr QMetaType metaType() const { return d.metaType(); }
210 constexpr void *data() { return d.data(); }
211 constexpr const void *data() const { return d.data(); }
212 constexpr const void *constData() const { return d.data(); }
213
214 template<Type type>
216 if constexpr (type == Undefined)
217 return QJSPrimitiveUndefined();
218 if constexpr (type == Null)
219 return QJSPrimitiveNull();
220 if constexpr (type == Boolean)
221 return toBoolean();
222 if constexpr (type == Integer)
223 return toInteger();
224 if constexpr (type == Double)
225 return toDouble();
226 if constexpr (type == String)
227 return toString();
228
229 Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
230 }
231
232 constexpr bool toBoolean() const
233 {
234 switch (type()) {
235 case Undefined: return false;
236 case Null: return false;
237 case Boolean: return asBoolean();
238 case Integer: return asInteger() != 0;
239 case Double: {
240 const double v = asDouble();
241 return !QJSNumberCoercion::equals(v, 0) && !std::isnan(v);
242 }
243 case String: return !asString().isEmpty();
244 }
245
246 // GCC 8.x does not treat __builtin_unreachable() as constexpr
247 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
248 Q_UNREACHABLE_RETURN(false);
249 #else
250 return false;
251 #endif
252 }
253
254 constexpr int toInteger() const
255 {
256 switch (type()) {
257 case Undefined: return 0;
258 case Null: return 0;
259 case Boolean: return asBoolean();
260 case Integer: return asInteger();
261 case Double: return QJSNumberCoercion::toInteger(asDouble());
262 case String: return numberFromString(asString()).toInteger();
263 }
264
265 // GCC 8.x does not treat __builtin_unreachable() as constexpr
266 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
267 Q_UNREACHABLE_RETURN(0);
268 #else
269 return 0;
270 #endif
271 }
272
273 constexpr double toDouble() const
274 {
275 switch (type()) {
276 case Undefined: return std::numeric_limits<double>::quiet_NaN();
277 case Null: return 0;
278 case Boolean: return asBoolean();
279 case Integer: return asInteger();
280 case Double: return asDouble();
281 case String: return numberFromString(asString()).toDouble();
282 }
283
284 // GCC 8.x does not treat __builtin_unreachable() as constexpr
285 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
286 Q_UNREACHABLE_RETURN({});
287 #else
288 return {};
289 #endif
290 }
291
293 {
294 switch (type()) {
295 case Undefined: return QStringLiteral("undefined");
296 case Null: return QStringLiteral("null");
297 case Boolean: return asBoolean() ? QStringLiteral("true") : QStringLiteral("false");
298 case Integer: return QString::number(asInteger());
299 case Double: {
300 const double result = asDouble();
301 if (std::isnan(result))
302 return QStringLiteral("NaN");
303 if (std::isfinite(result))
304 return toString(result);
305 if (result > 0)
306 return QStringLiteral("Infinity");
307 return QStringLiteral("-Infinity");
308 }
309 case String: return asString();
310 }
311
312 Q_UNREACHABLE_RETURN(QString());
313 }
314
316 {
317 switch (type()) {
318 case Undefined: return QVariant();
319 case Null: return QVariant::fromValue<std::nullptr_t>(nullptr);
320 case Boolean: return QVariant(asBoolean());
321 case Integer: return QVariant(asInteger());
322 case Double: return QVariant(asDouble());
323 case String: return QVariant(asString());
324 }
325
326 Q_UNREACHABLE_RETURN(QVariant());
327 }
328
330 const QJSPrimitiveValue &rhs)
331 {
332 return operate<AddOperators>(lhs, rhs);
333 }
334
336 const QJSPrimitiveValue &rhs)
337 {
338 return operate<SubOperators>(lhs, rhs);
339 }
340
342 const QJSPrimitiveValue &rhs)
343 {
344 return operate<MulOperators>(lhs, rhs);
345 }
346
348 const QJSPrimitiveValue &rhs)
349 {
350 return operate<DivOperators>(lhs, rhs);
351 }
352
354 const QJSPrimitiveValue &rhs)
355 {
356 switch (lhs.type()) {
357 case Null:
358 case Boolean:
359 case Integer:
360 switch (rhs.type()) {
361 case Boolean:
362 case Integer: {
363 const int leftInt = lhs.toInteger();
364 const int rightInt = rhs.toInteger();
365 if (leftInt >= 0 && rightInt > 0)
366 return leftInt % rightInt;
367 Q_FALLTHROUGH();
368 }
369 case Undefined:
370 case Null:
371 case Double:
372 case String:
373 break;
374 }
375 Q_FALLTHROUGH();
376 case Undefined:
377 case Double:
378 case String:
379 break;
380 }
381
382 return std::fmod(lhs.toDouble(), rhs.toDouble());
383 }
384
386 {
387 // ++a is modeled as a -= (-1) to avoid the potential string concatenation
388 return (*this = operate<SubOperators>(*this, -1));
389 }
390
392 {
393 // a++ is modeled as a -= (-1) to avoid the potential string concatenation
394 QJSPrimitiveValue other = operate<SubOperators>(*this, -1);
395 std::swap(other, *this);
396 return +other; // We still need to coerce the original value.
397 }
398
400 {
401 return (*this = operate<SubOperators>(*this, 1));
402 }
403
405 {
406 QJSPrimitiveValue other = operate<SubOperators>(*this, 1);
407 std::swap(other, *this);
408 return +other; // We still need to coerce the original value.
409 }
410
412 {
413 // +a is modeled as a -= 0. That should force it to number.
414 return (*this = operate<SubOperators>(*this, 0));
415 }
416
418 {
419 return (*this = operate<MulOperators>(*this, -1));
420 }
421
422 constexpr bool strictlyEquals(const QJSPrimitiveValue &other) const
423 {
424 const Type myType = type();
425 const Type otherType = other.type();
426
427 if (myType != otherType) {
428 // int -> double promotion is OK in strict mode
429 if (myType == Double && otherType == Integer)
430 return strictlyEquals(double(other.asInteger()));
431 if (myType == Integer && otherType == Double)
432 return QJSPrimitiveValue(double(asInteger())).strictlyEquals(other);
433 return false;
434 }
435
436 switch (myType) {
437 case Undefined:
438 case Null:
439 return true;
440 case Boolean:
441 return asBoolean() == other.asBoolean();
442 case Integer:
443 return asInteger() == other.asInteger();
444 case Double: {
445 const double l = asDouble();
446 const double r = other.asDouble();
447 if (std::isnan(l) || std::isnan(r))
448 return false;
449 if (qIsNull(l) && qIsNull(r))
450 return true;
451 return QJSNumberCoercion::equals(l, r);
452 }
453 case String:
454 return asString() == other.asString();
455 }
456
457 return false;
458 }
459
460 // Loose operator==, in contrast to strict ===
461 constexpr bool equals(const QJSPrimitiveValue &other) const
462 {
463 const Type myType = type();
464 const Type otherType = other.type();
465
466 if (myType == otherType)
467 return strictlyEquals(other);
468
469 switch (myType) {
470 case Undefined:
471 return otherType == Null;
472 case Null:
473 return otherType == Undefined;
474 case Boolean:
475 return QJSPrimitiveValue(int(asBoolean())).equals(other);
476 case Integer:
477 // prefer rhs bool -> int promotion over promoting both to double
478 return otherType == Boolean
479 ? QJSPrimitiveValue(asInteger()).equals(int(other.asBoolean()))
480 : QJSPrimitiveValue(double(asInteger())).equals(other);
481 case Double:
482 // Promote the other side to double (or recognize lhs as undefined/null)
483 return other.equals(*this);
484 case String:
485 return numberFromString(asString()).parsedEquals(other);
486 }
487
488 return false;
489 }
490
491 friend constexpr inline bool operator==(const QJSPrimitiveValue &lhs, const
493 {
494 return lhs.strictlyEquals(rhs);
495 }
496
497 friend constexpr inline bool operator!=(const QJSPrimitiveValue &lhs,
498 const QJSPrimitiveValue &rhs)
499 {
500 return !lhs.strictlyEquals(rhs);
501 }
502
503 friend constexpr inline bool operator<(const QJSPrimitiveValue &lhs,
504 const QJSPrimitiveValue &rhs)
505 {
506 switch (lhs.type()) {
507 case Undefined:
508 return false;
509 case Null: {
510 switch (rhs.type()) {
511 case Undefined: return false;
512 case Null: return false;
513 case Boolean: return 0 < int(rhs.asBoolean());
514 case Integer: return 0 < rhs.asInteger();
515 case Double: return double(0) < rhs.asDouble();
516 case String: return double(0) < rhs.toDouble();
517 }
518 break;
519 }
520 case Boolean: {
521 switch (rhs.type()) {
522 case Undefined: return false;
523 case Null: return int(lhs.asBoolean()) < 0;
524 case Boolean: return lhs.asBoolean() < rhs.asBoolean();
525 case Integer: return int(lhs.asBoolean()) < rhs.asInteger();
526 case Double: return double(lhs.asBoolean()) < rhs.asDouble();
527 case String: return double(lhs.asBoolean()) < rhs.toDouble();
528 }
529 break;
530 }
531 case Integer: {
532 switch (rhs.type()) {
533 case Undefined: return false;
534 case Null: return lhs.asInteger() < 0;
535 case Boolean: return lhs.asInteger() < int(rhs.asBoolean());
536 case Integer: return lhs.asInteger() < rhs.asInteger();
537 case Double: return double(lhs.asInteger()) < rhs.asDouble();
538 case String: return double(lhs.asInteger()) < rhs.toDouble();
539 }
540 break;
541 }
542 case Double: {
543 switch (rhs.type()) {
544 case Undefined: return false;
545 case Null: return lhs.asDouble() < double(0);
546 case Boolean: return lhs.asDouble() < double(rhs.asBoolean());
547 case Integer: return lhs.asDouble() < double(rhs.asInteger());
548 case Double: return lhs.asDouble() < rhs.asDouble();
549 case String: return lhs.asDouble() < rhs.toDouble();
550 }
551 break;
552 }
553 case String: {
554 switch (rhs.type()) {
555 case Undefined: return false;
556 case Null: return lhs.toDouble() < double(0);
557 case Boolean: return lhs.toDouble() < double(rhs.asBoolean());
558 case Integer: return lhs.toDouble() < double(rhs.asInteger());
559 case Double: return lhs.toDouble() < rhs.asDouble();
560 case String: return lhs.asString() < rhs.asString();
561 }
562 break;
563 }
564 }
565
566 return false;
567 }
568
569 friend constexpr inline bool operator>(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
570 {
571 return rhs < lhs;
572 }
573
574 friend constexpr inline bool operator<=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
575 {
576 if (lhs.type() == String) {
577 if (rhs.type() == String)
578 return lhs.asString() <= rhs.asString();
579 else
580 return numberFromString(lhs.asString()) <= rhs;
581 }
582 if (rhs.type() == String)
583 return lhs <= numberFromString(rhs.asString());
584
585 if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined())
586 return false;
587 return !(lhs > rhs);
588 }
589
590 friend constexpr inline bool operator>=(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
591 {
592 if (lhs.type() == String) {
593 if (rhs.type() == String)
594 return lhs.asString() >= rhs.asString();
595 else
596 return numberFromString(lhs.asString()) >= rhs;
597 }
598 if (rhs.type() == String)
599 return lhs >= numberFromString(rhs.asString());
600
601 if (lhs.isNanOrUndefined() || rhs.isNanOrUndefined())
602 return false;
603 return !(lhs < rhs);
604 }
605
606private:
607 friend class QJSManagedValue;
608 friend class QJSValue;
609 friend struct QV4::ExecutionEngine;
610
611 constexpr bool asBoolean() const { return d.getBool(); }
612 constexpr int asInteger() const { return d.getInt(); }
613 constexpr double asDouble() const { return d.getDouble(); }
614 QString asString() const { return d.getString(); }
615
616 constexpr bool parsedEquals(const QJSPrimitiveValue &other) const
617 {
618 return type() != Undefined && equals(other);
619 }
620
621 static QJSPrimitiveValue numberFromString(const QString &string)
622 {
623 bool ok;
624 const int intValue = string.toInt(&ok);
625 if (ok)
626 return intValue;
627
628 const double doubleValue = string.toDouble(&ok);
629 if (ok)
630 return doubleValue;
631 if (string.isEmpty())
632 return 0;
633 if (string == QStringLiteral("Infinity"))
634 return std::numeric_limits<double>::infinity();
635 if (string == QStringLiteral("-Infinity"))
636 return -std::numeric_limits<double>::infinity();
637 if (string == QStringLiteral("NaN"))
638 return std::numeric_limits<double>::quiet_NaN();
639 return QJSPrimitiveUndefined();
640 }
641
642 static Q_QML_EXPORT QString toString(double d);
643
644 template<typename Operators, typename Lhs, typename Rhs>
645 static QJSPrimitiveValue operateOnIntegers(const QJSPrimitiveValue &lhs,
646 const QJSPrimitiveValue &rhs)
647 {
648 int result;
649 if (Operators::opOverflow(lhs.d.get<Lhs>(), rhs.d.get<Rhs>(), &result))
650 return Operators::op(lhs.d.get<Lhs>(), rhs.d.get<Rhs>());
651 return result;
652 }
653
654 template<typename Operators>
655 static QJSPrimitiveValue operate(const QJSPrimitiveValue &lhs, const QJSPrimitiveValue &rhs)
656 {
657 switch (lhs.type()) {
658 case Undefined:
659 switch (rhs.type()) {
660 case Undefined: return std::numeric_limits<double>::quiet_NaN();
661 case Null: return std::numeric_limits<double>::quiet_NaN();
662 case Boolean: return std::numeric_limits<double>::quiet_NaN();
663 case Integer: return std::numeric_limits<double>::quiet_NaN();
664 case Double: return std::numeric_limits<double>::quiet_NaN();
665 case String: return Operators::op(QJSPrimitiveUndefined(), rhs.asString());
666 }
667 break;
668 case Null:
669 switch (rhs.type()) {
670 case Undefined: return std::numeric_limits<double>::quiet_NaN();
671 case Null: return operateOnIntegers<Operators, int, int>(0, 0);
672 case Boolean: return operateOnIntegers<Operators, int, bool>(0, rhs);
673 case Integer: return operateOnIntegers<Operators, int, int>(0, rhs);
674 case Double: return Operators::op(0, rhs.asDouble());
675 case String: return Operators::op(QJSPrimitiveNull(), rhs.asString());
676 }
677 break;
678 case Boolean:
679 switch (rhs.type()) {
680 case Undefined: return std::numeric_limits<double>::quiet_NaN();
681 case Null: return operateOnIntegers<Operators, bool, int>(lhs, 0);
682 case Boolean: return operateOnIntegers<Operators, bool, bool>(lhs, rhs);
683 case Integer: return operateOnIntegers<Operators, bool, int>(lhs, rhs);
684 case Double: return Operators::op(lhs.asBoolean(), rhs.asDouble());
685 case String: return Operators::op(lhs.asBoolean(), rhs.asString());
686 }
687 break;
688 case Integer:
689 switch (rhs.type()) {
690 case Undefined: return std::numeric_limits<double>::quiet_NaN();
691 case Null: return operateOnIntegers<Operators, int, int>(lhs, 0);
692 case Boolean: return operateOnIntegers<Operators, int, bool>(lhs, rhs);
693 case Integer: return operateOnIntegers<Operators, int, int>(lhs, rhs);
694 case Double: return Operators::op(lhs.asInteger(), rhs.asDouble());
695 case String: return Operators::op(lhs.asInteger(), rhs.asString());
696 }
697 break;
698 case Double:
699 switch (rhs.type()) {
700 case Undefined: return std::numeric_limits<double>::quiet_NaN();
701 case Null: return Operators::op(lhs.asDouble(), 0);
702 case Boolean: return Operators::op(lhs.asDouble(), rhs.asBoolean());
703 case Integer: return Operators::op(lhs.asDouble(), rhs.asInteger());
704 case Double: return Operators::op(lhs.asDouble(), rhs.asDouble());
705 case String: return Operators::op(lhs.asDouble(), rhs.asString());
706 }
707 break;
708 case String:
709 switch (rhs.type()) {
710 case Undefined: return Operators::op(lhs.asString(), QJSPrimitiveUndefined());
711 case Null: return Operators::op(lhs.asString(), QJSPrimitiveNull());
712 case Boolean: return Operators::op(lhs.asString(), rhs.asBoolean());
713 case Integer: return Operators::op(lhs.asString(), rhs.asInteger());
714 case Double: return Operators::op(lhs.asString(), rhs.asDouble());
715 case String: return Operators::op(lhs.asString(), rhs.asString());
716 }
717 break;
718 }
719
720 Q_UNREACHABLE_RETURN(QJSPrimitiveUndefined());
721 }
722
723 constexpr bool isNanOrUndefined() const
724 {
725 switch (type()) {
726 case Undefined: return true;
727 case Null: return false;
728 case Boolean: return false;
729 case Integer: return false;
730 case Double: return std::isnan(asDouble());
731 case String: return false;
732 }
733 // GCC 8.x does not treat __builtin_unreachable() as constexpr
734 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
735 Q_UNREACHABLE_RETURN(false);
736 #else
737 return false;
738 #endif
739 }
740
741 struct QJSPrimitiveValuePrivate
742 {
743 // Can't be default because QString has a non-trivial ctor.
744 constexpr QJSPrimitiveValuePrivate() noexcept {}
745
746 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveUndefined) noexcept {}
747 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(QJSPrimitiveNull) noexcept
748 : m_type(Null) {}
749 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(bool b) noexcept
750 : m_bool(b), m_type(Boolean) {}
751 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(int i) noexcept
752 : m_int(i), m_type(Integer) {}
753 Q_IMPLICIT constexpr QJSPrimitiveValuePrivate(double d) noexcept
754 : m_double(d), m_type(Double) {}
755 Q_IMPLICIT QJSPrimitiveValuePrivate(QString s) noexcept
756 : m_string(std::move(s)), m_type(String) {}
757
758 constexpr QJSPrimitiveValuePrivate(const QJSPrimitiveValuePrivate &other) noexcept
759 : m_type(other.m_type)
760 {
761 // Not copy-and-swap since swap() would be much more complicated.
762 if (!assignSimple(other))
763 new (&m_string) QString(other.m_string);
764 }
765
766 constexpr QJSPrimitiveValuePrivate(QJSPrimitiveValuePrivate &&other) noexcept
767 : m_type(other.m_type)
768 {
769 // Not move-and-swap since swap() would be much more complicated.
770 if (!assignSimple(other))
771 new (&m_string) QString(std::move(other.m_string));
772 }
773
774 constexpr QJSPrimitiveValuePrivate &operator=(const QJSPrimitiveValuePrivate &other) noexcept
775 {
776 if (this == &other)
777 return *this;
778
779 if (m_type == String) {
780 if (other.m_type == String) {
781 m_type = other.m_type;
782 m_string = other.m_string;
783 return *this;
784 }
785 m_string.~QString();
786 }
787
788 m_type = other.m_type;
789 if (!assignSimple(other))
790 new (&m_string) QString(other.m_string);
791 return *this;
792 }
793
794 constexpr QJSPrimitiveValuePrivate &operator=(QJSPrimitiveValuePrivate &&other) noexcept
795 {
796 if (this == &other)
797 return *this;
798
799 if (m_type == String) {
800 if (other.m_type == String) {
801 m_type = other.m_type;
802 m_string = std::move(other.m_string);
803 return *this;
804 }
805 m_string.~QString();
806 }
807
808 m_type = other.m_type;
809 if (!assignSimple(other))
810 new (&m_string) QString(std::move(other.m_string));
811 return *this;
812 }
813
814 ~QJSPrimitiveValuePrivate()
815 {
816 if (m_type == String)
817 m_string.~QString();
818 }
819
820 constexpr Type type() const noexcept { return m_type; }
821 constexpr bool getBool() const noexcept { return m_bool; }
822 constexpr int getInt() const noexcept { return m_int; }
823 constexpr double getDouble() const noexcept { return m_double; }
824 QString getString() const noexcept { return m_string; }
825
826 template<typename T>
827 constexpr T get() const noexcept {
828 if constexpr (std::is_same_v<T, QJSPrimitiveUndefined>)
829 return QJSPrimitiveUndefined();
830 else if constexpr (std::is_same_v<T, QJSPrimitiveNull>)
831 return QJSPrimitiveNull();
832 else if constexpr (std::is_same_v<T, bool>)
833 return getBool();
834 else if constexpr (std::is_same_v<T, int>)
835 return getInt();
836 else if constexpr (std::is_same_v<T, double>)
837 return getDouble();
838 else if constexpr (std::is_same_v<T, QString>)
839 return getString();
840
841 // GCC 8.x does not treat __builtin_unreachable() as constexpr
842 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
843 Q_UNREACHABLE_RETURN(T());
844 #else
845 return T();
846 #endif
847 }
848
849 constexpr QMetaType metaType() const noexcept {
850 switch (m_type) {
851 case Undefined:
852 return QMetaType();
853 case Null:
854 return QMetaType::fromType<std::nullptr_t>();
855 case Boolean:
856 return QMetaType::fromType<bool>();
857 case Integer:
858 return QMetaType::fromType<int>();
859 case Double:
860 return QMetaType::fromType<double>();
861 case String:
862 return QMetaType::fromType<QString>();
863 }
864
865 // GCC 8.x does not treat __builtin_unreachable() as constexpr
866 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
867 Q_UNREACHABLE_RETURN(QMetaType());
868 #else
869 return QMetaType();
870 #endif
871 }
872
873 constexpr void *data() noexcept {
874 switch (m_type) {
875 case Undefined:
876 case Null:
877 return nullptr;
878 case Boolean:
879 return &m_bool;
880 case Integer:
881 return &m_int;
882 case Double:
883 return &m_double;
884 case String:
885 return &m_string;
886 }
887
888 // GCC 8.x does not treat __builtin_unreachable() as constexpr
889 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
890 Q_UNREACHABLE_RETURN(nullptr);
891 #else
892 return nullptr;
893 #endif
894 }
895
896 constexpr const void *data() const noexcept {
897 switch (m_type) {
898 case Undefined:
899 case Null:
900 return nullptr;
901 case Boolean:
902 return &m_bool;
903 case Integer:
904 return &m_int;
905 case Double:
906 return &m_double;
907 case String:
908 return &m_string;
909 }
910
911 // GCC 8.x does not treat __builtin_unreachable() as constexpr
912 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
913 Q_UNREACHABLE_RETURN(nullptr);
914 #else
915 return nullptr;
916 #endif
917 }
918
919 private:
920 constexpr bool assignSimple(const QJSPrimitiveValuePrivate &other) noexcept
921 {
922 switch (other.m_type) {
923 case Undefined:
924 case Null:
925 return true;
926 case Boolean:
927 m_bool = other.m_bool;
928 return true;
929 case Integer:
930 m_int = other.m_int;
931 return true;
932 case Double:
933 m_double = other.m_double;
934 return true;
935 case String:
936 return false;
937 }
938
939 // GCC 8.x does not treat __builtin_unreachable() as constexpr
940 #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
941 Q_UNREACHABLE_RETURN(false);
942 #else
943 return false;
944 #endif
945 }
946
947 union {
948 bool m_bool;
949 int m_int;
950 double m_double;
951 QString m_string;
952
953 // Dummy value to trigger initialization of the whole storage.
954 // We don't want to see maybe-uninitialized warnings every time we access m_string.
955 std::byte m_zeroInitialize[sizeof(QString)] = {};
956 };
957
958 Type m_type = Undefined;
959 };
960
961 QJSPrimitiveValuePrivate d;
962};
963
964namespace QQmlPrivate {
965 // TODO: Make this constexpr once std::isnan is constexpr.
966 inline double jsExponentiate(double base, double exponent)
967 {
968 constexpr double qNaN = std::numeric_limits<double>::quiet_NaN();
969 constexpr double inf = std::numeric_limits<double>::infinity();
970
971 if (qIsNull(exponent))
972 return 1.0;
973
974 if (std::isnan(exponent))
975 return qNaN;
976
977 if (QJSNumberCoercion::equals(base, 1.0) || QJSNumberCoercion::equals(base, -1.0))
978 return std::isinf(exponent) ? qNaN : std::pow(base, exponent);
979
980 if (!qIsNull(base))
981 return std::pow(base, exponent);
982
983 if (std::copysign(1.0, base) > 0.0)
984 return exponent < 0.0 ? inf : std::pow(base, exponent);
985
986 if (exponent < 0.0)
987 return QJSNumberCoercion::equals(std::fmod(-exponent, 2.0), 1.0) ? -inf : inf;
988
989 return QJSNumberCoercion::equals(std::fmod(exponent, 2.0), 1.0)
990 ? std::copysign(0, -1.0)
991 : 0.0;
992 }
993}
994
995QT_END_NAMESPACE
996
997#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....