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
qqmlvaluetypewrapper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
6
7#include <private/qqmlbinding_p.h>
8#include <private/qqmlbuiltinfunctions_p.h>
9#include <private/qqmlvaluetype_p.h>
10
11#include <private/qv4alloca_p.h>
12#include <private/qv4arraybuffer_p.h>
13#include <private/qv4dateobject_p.h>
14#include <private/qv4engine_p.h>
15#include <private/qv4functionobject_p.h>
16#include <private/qv4identifiertable_p.h>
17#include <private/qv4jsonobject_p.h>
18#include <private/qv4lookup_p.h>
19#include <private/qv4qobjectwrapper_p.h>
20#include <private/qv4stackframe_p.h>
21#include <private/qv4variantobject_p.h>
22
23#include <QtCore/qline.h>
24#include <QtCore/qsize.h>
25#include <QtCore/qdatetime.h>
26#include <QtCore/qloggingcategory.h>
27
28#if QT_CONFIG(regularexpression)
29#include <private/qv4regexpobject_p.h>
30#endif
31
33
34DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper);
35
36namespace QV4 {
37
39{
42}
43
45{
48}
49
55
57{
59
62 // This is a stale VariantReference. That is, the variant has been
63 // overwritten with a different type in the meantime.
64 // We need to modify this reference to the updated value type, if
65 // possible, or return false if it is not a value type.
69 setGadgetPtr(nullptr);
72 if (!mo)
73 return false;
74 } else {
75 return false;
76 }
77 }
78
80 return true;
81}
82
84{
85 if (!gadgetPtr()) {
86 setGadgetPtr(metaType().create(nullptr));
87 }
88 return gadgetPtr();
89}
90
92{
93 // If locations are enforced we only read once
95}
96
98{
100}
101
104{
107
108 // Either we're enforcing the location, then we have to read right away.
109 // Or we don't then we lazy-load. In neither case we pass any data.
111 nullptr, cloneFrom->metaType(), cloneFrom->metaObject(),
116 return r->asReturnedValue();
117}
118
129
131 Object *object, QMetaObject::Call call, int index, void **a)
132{
135
136 switch (call) {
141 if (wrapper->d()->object())
142 wrapper->d()->readReference();
143 break;
144 default:
145 break;
146 }
147
148 const QMetaObject *mo = wrapper->d()->metaObject();
149 if (!mo->d.static_metacall)
150 return 0;
151
152 mo->d.static_metacall(static_cast<QObject *>(wrapper->d()->gadgetPtr()), call, index, a);
153
154 switch (call) {
156 break;
159 if (wrapper->d()->object())
161 break;
164 if (wrapper->d()->object())
165 wrapper->d()->writeBack();
166 break;
167 default:
168 break;
169 }
170
171 return -1;
172}
173
177{
180
181 if (!type.isValid()) {
182 return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
184 }
185
186 // If data is given explicitly, we assume it has just been read from the property
191 if (!data && r->d()->enforcesLocation())
193 return r->asReturnedValue();
194}
195
198{
201
202 if (!type.isValid()) {
203 return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
205 }
206
209 data, type, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
210 return r->asReturnedValue();
211}
212
214{
215 if (d()->isReference() && !readReferenceValue())
216 return QVariant();
217 return d()->toVariant();
218}
219
221{
222 if (d()->isReference() && !readReferenceValue())
223 return false;
224 const QMetaType type = d()->metaType();
227 return true;
228}
229
231{
234
236 return lv->isEqual(rv->d()->data());
237
239 return lv->isEqual(v->toVariant());
240
241 return false;
242}
243
245{
246 if (!id.isString())
247 return Object::virtualHasProperty(m, id);
249 auto wrapper = static_cast<const QQmlValueTypeWrapper *>(m);
250 if (auto mo = wrapper->d()->metaObject())
251 if (mo->indexOfProperty(id.toQString().toUtf8()) != -1)
252 return true;
253
254 /* we don't want to fallback to QObject::virtualHasProperty
255 as that would end up calling getOwnProperty which is wasteful,
256 as it calls our own virtualGetOwnProperty.
257 As we know that our own properties are only those found on the meta-object,
258 we can instead skip the call, and simply check whether the property exists
259 on the prototype.
260 */
261 Scope scope(m->engine());
263 o = o->getPrototypeOf();
264 if (o)
265 return o->hasProperty(id);
266
267 return false;
268}
269
270static Heap::ReferenceObject::Flags referenceFlags(const QMetaObject *metaObject, int index)
271{
272 return metaObject->property(index).isWritable()
273 ? (Heap::ReferenceObject::CanWriteBack | Heap::ReferenceObject::EnforcesLocation)
274 : Heap::ReferenceObject::EnforcesLocation;
275}
276
278 const QMetaObject *metaObject, Heap::QQmlValueTypeWrapper *valueTypeWrapper,
279 int index, void **args)
280{
281 metaObject->d.static_metacall(
282 reinterpret_cast<QObject*>(
283 valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty, index, args);
284}
285
289{
290 if (isFunction) {
291 // calling a Q_INVOKABLE function of a value type
293 }
294
296 int index = coreIndex;
297
298 const auto wrapChar16 = [engine](char16_t c) {
299 return engine->newString(QChar(c));
300 };
301 const auto wrapQObject = [engine](QObject *object) {
303 };
304 const auto wrapJsonValue = [engine](const QJsonValue &value) {
306 };
307 const auto wrapJsonObject = [engine](const QJsonObject &object) {
309 };
310 const auto wrapJsonArray = [engine](const QJsonArray &array) {
312 };
313
314 const auto wrapQDateTime = [&](const QDateTime &dateTime) {
315 return engine->newDateObject(
317 };
318 const auto wrapQDate = [&](QDate date) {
319 return engine->newDateObject(
321 };
322 const auto wrapQTime = [&](QTime time) {
323 return engine->newDateObject(
325 };
326
327#define VALUE_TYPE_LOAD(metatype, cpptype, constructor)
328 case metatype: {
329 cpptype v;
330 void *args[] = { &v, nullptr };
331 doStaticReadCall(metaObject, valueTypeWrapper, index, args);
332 return QV4::Encode(constructor(v));
333 }
334
337
338 const int metaTypeId = isEnum
342 : metaType.id();
343
344 switch (metaTypeId) {
346 case QMetaType::Void:
347 return Encode::undefined();
348 case QMetaType::Nullptr:
349 case QMetaType::VoidStar:
350 return Encode::null();
351 VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
352 VALUE_TYPE_LOAD(QMetaType::Int, int, int);
354 VALUE_TYPE_LOAD(QMetaType::Long, long, double);
358 VALUE_TYPE_LOAD(QMetaType::Double, double, double);
361 VALUE_TYPE_LOAD(QMetaType::Float, float, float);
362 VALUE_TYPE_LOAD(QMetaType::Short, short, int);
363 VALUE_TYPE_LOAD(QMetaType::UShort, unsigned short, int);
364 VALUE_TYPE_LOAD(QMetaType::Char, char, int);
365 VALUE_TYPE_LOAD(QMetaType::UChar, unsigned char, int);
366 VALUE_TYPE_LOAD(QMetaType::SChar, signed char, int);
372#if QT_CONFIG(regularexpression)
374#endif
379 case QMetaType::QPixmap:
380 case QMetaType::QImage: {
382 void *args[] = { v.data(), nullptr };
385 }
386 case QMetaType::QVariant: {
387 QVariant v;
388 void *args[] = { &v, nullptr };
390 return engine->fromVariant(
393 }
394 default:
395 break;
396 }
397
399 void *args[] = { v.data(), nullptr };
402#undef VALUE_TYPE_LOAD
403}
404
406{
407 if (id.isString()) {
408 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
409 Q_ASSERT(r);
410
412 if (!result.isValid())
413 return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it.
414
415 if (!p)
416 return Attr_Data; // Property exists, but we're not interested in the value
417
418 if (!r->d()->isReference() || r->readReferenceValue()) {
419 // Property exists, and we can retrieve it
421 r->engine(), r->d(), result.propType(), result.coreIndex(),
423 } else {
424 // Property exists, but we can't retrieve it. Make it undefined.
425 p->value = Encode::undefined();
426 }
427
428 return Attr_Data;
429 }
430
431 return QV4::Object::virtualGetOwnProperty(m, id, p);
432}
433
435{
438 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
439
440};
441
442PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) {
443 const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o);
444
445 if (that->d()->isReference() && !that->readReferenceValue())
446 return PropertyKey::invalid();
447
448 const QMetaObject *mo = that->d()->metaObject();
449 // We don't return methods, ie. they are not visible when iterating
450 const int propertyCount = mo->propertyCount();
451 if (propertyIndex < propertyCount) {
452 Scope scope(that->engine());
453 QMetaProperty p = mo->property(propertyIndex); // TODO: Implement and use QBasicMetaProperty
454 ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(p.name())));
456 if (attrs)
457 *attrs = QV4::Attr_Data;
458 if (pd) {
459 QQmlPropertyData data;
460 data.load(p);
461 pd->value = QQmlValueTypeWrapper::getGadgetProperty(
462 that->engine(), that->d(), data.propType(), data.coreIndex(), data.isFunction(),
463 data.isEnum());
464 }
465 return propName->toPropertyKey();
466 }
467
468 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
469}
470
471
477
479{
480 if (d()->isReference() && !readReferenceValue())
481 return false;
482 int id1 = value.metaType().id();
483 QVariant v = d()->toVariant();
484 int id2 = v.metaType().id();
485 if (id1 != id2) {
486 // conversions for weak comparison
487 switch (id1) {
488 case QMetaType::QPoint:
489 if (id2 == QMetaType::QPointF)
490 return value.value<QPointF>() == v.value<QPointF>();
491 break;
492 case QMetaType::QPointF:
493 if (id2 == QMetaType::QPoint)
494 return value.value<QPointF>() == v.value<QPointF>();
495 break;
496 case QMetaType::QRect:
497 if (id2 == QMetaType::QRectF)
498 return value.value<QRectF>() == v.value<QRectF>();
499 break;
500 case QMetaType::QRectF:
501 if (id2 == QMetaType::QRect)
502 return value.value<QRectF>() == v.value<QRectF>();
503 break;
504 case QMetaType::QLine:
505 if (id2 == QMetaType::QLineF)
506 return value.value<QLineF>() == v.value<QLineF>();
507 break;
508 case QMetaType::QLineF:
509 if (id2 == QMetaType::QLine)
510 return value.value<QLineF>() == v.value<QLineF>();
511 break;
512 case QMetaType::QSize:
513 if (id2 == QMetaType::QSizeF)
514 return value.value<QSizeF>() == v.value<QSizeF>();
515 break;
516 case QMetaType::QSizeF:
517 if (id2 == QMetaType::QSize)
518 return value.value<QSizeF>() == v.value<QSizeF>();
519 break;
520 default:
521 break;
522 }
523 }
524 return (value == v);
525}
526
528{
529 return d()->metaType().id();
530}
531
533{
534 return d()->metaType();
535}
536
538{
539 bool destructGadgetOnExit = false;
540 auto cleanup = qScopeGuard([&]() {
542 d()->setDirty(true);
543 d()->metaType().destruct(d()->gadgetPtr());
544 d()->setGadgetPtr(nullptr);
545 }
546 });
547
549
550 if (d()->isReference()) {
552 QT_WARNING_DISABLE_GCC("-Walloca-larger-than="); // for size = alignment = 0
553 if (!d()->gadgetPtr()) {
554 const size_t size = d()->metaType().sizeOf();
555 const size_t alignment = d()->metaType().alignOf();
556 size_t space = size + alignment - 1;
561 d()->metaType().construct(d()->gadgetPtr(), nullptr);
563 }
565 if (!readReferenceValue())
566 return false;
567 }
568
569 int flags = 0;
570 int status = -1;
571 void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
573 return true;
574}
575
593
595{
596 const Object *o = thisObject->as<Object>();
597 if (!o)
598 return b->engine()->throwTypeError();
600 if (!w)
601 return b->engine()->throwTypeError();
602
603 if (w->d()->isReference() && !w->readReferenceValue())
605
607 if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
609 result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
610 const QMetaObject *mo = w->d()->metaObject();
611 const int propCount = mo->propertyCount();
612 for (int i = 0; i < propCount; ++i) {
613 if (mo->property(i).isDesignable()) {
615 if (i > 0)
616 result += QLatin1String(", ");
617 result += value.toString();
618 }
619 }
620 result += QLatin1Char(')');
621 }
622 return Encode(b->engine()->newString(result));
623}
624
626 Lookup *lookup)
627{
630 if (!id.isString())
632
633 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
635 Scope scope(v4);
637
638 // Note: readReferenceValue() can change the reference->type.
639 if (r->d()->isReference() && !r->readReferenceValue())
641
643 if (!result.isValid())
645
647 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
654 return lookup->getter(engine, *object);
655}
656
662
664{
666
667 if (!id.isString())
669
670 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
672
673 // Note: readReferenceValue() can change the reference->type.
674 if (r->d()->isReference() && !r->readReferenceValue())
676
678 if (!result.isValid())
680
681 if (hasProperty)
682 *hasProperty = true;
683
685}
686
688{
689 if (!id.isString())
690 return Object::virtualPut(m, id, value, receiver);
691
693 ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
694 Scope scope(v4);
695 if (scope.hasException())
696 return false;
697
699 Heap::Object *heapObject = nullptr;
700 if (r->d()->isReference()) {
701 heapObject = r->d()->object();
702 if (!r->readReferenceValue() || !r->d()->canWriteBack())
703 return false;
704 }
705
706 const QMetaObject *metaObject = r->d()->metaObject();
708 if (!pd.isValid())
709 return false;
710
711 if (heapObject) {
712 QObject *referenceObject = nullptr;
714 const int referencePropertyIndex = r->d()->property();
716 if (o) {
718 } else {
720 if (t)
722 }
723
724 if (f) {
725 if (!f->isBinding()) {
726 // assigning a JS function to a non-var-property is not allowed.
727 QString error = QStringLiteral("Cannot assign JavaScript function to value-type property");
729 v4->throwError(e);
730 return false;
731 }
732
733 if (!referenceObject) {
734 QString error = QStringLiteral("Cannot create binding on nested value type property");
736 v4->throwError(e);
737 return false;
738 }
739
743
745
750
752
757 if (f->isBoundFunction())
762 return true;
763 } else if (referenceObject) {
768 binding && !binding->isSticky()) {
770 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
771 const auto stackFrame = v4->currentStackFrame;
773 "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
778 }
779 }
782 }
783 }
784
787 if (value.isUndefined() && pd.isResettable()) {
788 property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
789 if (heapObject)
790 r->d()->writeBack(pd.coreIndex());
791 return true;
792 }
793
795
797 v = v.toInt();
798
799 void *gadget = r->d()->gadgetPtr();
800 const QMetaType type = v.metaType();
802 const QString error = QLatin1String("Cannot assign ") +
804 QLatin1String(" to ") +
807 return true;
808 }
809
810 if (heapObject)
811 r->d()->writeBack(pd.coreIndex());
812
813 return true;
814}
815
816} // namespace QV4
817
818QT_END_NAMESPACE
Combined button and popup list for selecting options.
Definition qjsvalue.h:24
static Heap::ReferenceObject::Flags referenceFlags(const QMetaObject *metaObject, int index)
static void doStaticReadCall(const QMetaObject *metaObject, Heap::QQmlValueTypeWrapper *valueTypeWrapper, int index, void **args)
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor)
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
~QQmlValueTypeWrapperOwnPropertyKeyIterator() override=default