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{
540 bool destructGadgetOnExit = false;
541 auto cleanup = qScopeGuard([&]() {
543 d()->setDirty(true);
544 d()->metaType().destruct(d()->gadgetPtr());
545 d()->setGadgetPtr(nullptr);
546 }
547 });
548
550
551 if (d()->isReference()) {
553 QT_WARNING_DISABLE_GCC("-Walloca-larger-than="); // for size = alignment = 0
554 if (!d()->gadgetPtr()) {
555 const size_t size = d()->metaType().sizeOf();
556 const size_t alignment = d()->metaType().alignOf();
557 size_t space = size + alignment - 1;
562 d()->metaType().construct(d()->gadgetPtr(), nullptr);
564 }
566 if (!readReferenceValue())
567 return false;
568 }
569
570 int flags = 0;
571 int status = -1;
572 void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
574 return true;
575}
576
594
596{
597 const Object *o = thisObject->as<Object>();
598 if (!o)
599 return b->engine()->throwTypeError();
601 if (!w)
602 return b->engine()->throwTypeError();
603
604 if (w->d()->isReference() && !w->readReferenceValue())
606
608 if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
610 result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
611 const QMetaObject *mo = w->d()->metaObject();
612 const int propCount = mo->propertyCount();
613 for (int i = 0; i < propCount; ++i) {
614 if (mo->property(i).isDesignable()) {
616 if (i > 0)
617 result += QLatin1String(", ");
618 result += value.toString();
619 }
620 }
621 result += QLatin1Char(')');
622 }
623 return Encode(b->engine()->newString(result));
624}
625
627 Lookup *lookup)
628{
631 if (!id.isString())
633
634 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
636 Scope scope(v4);
638
639 // Note: readReferenceValue() can change the reference->type.
640 if (r->d()->isReference() && !r->readReferenceValue())
642
644 if (!result.isValid())
646
648 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
655 return lookup->getter(engine, *object);
656}
657
663
665{
667
668 if (!id.isString())
670
671 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
673
674 // Note: readReferenceValue() can change the reference->type.
675 if (r->d()->isReference() && !r->readReferenceValue())
677
679 if (!result.isValid())
681
682 if (hasProperty)
683 *hasProperty = true;
684
686}
687
689{
690 if (!id.isString())
691 return Object::virtualPut(m, id, value, receiver);
692
694 ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
695 Scope scope(v4);
696 if (scope.hasException())
697 return false;
698
700 Heap::Object *heapObject = nullptr;
701 if (r->d()->isReference()) {
702 heapObject = r->d()->object();
703 if (!r->readReferenceValue() || !r->d()->canWriteBack())
704 return false;
705 }
706
707 const QMetaObject *metaObject = r->d()->metaObject();
709 if (!pd.isValid())
710 return false;
711
712 if (heapObject) {
713 QObject *referenceObject = nullptr;
715 const int referencePropertyIndex = r->d()->property();
717 if (o) {
719 } else {
721 if (t)
723 }
724
725 if (f) {
726 if (!f->isBinding()) {
727 // assigning a JS function to a non-var-property is not allowed.
728 QString error = QStringLiteral("Cannot assign JavaScript function to value-type property");
730 v4->throwError(e);
731 return false;
732 }
733
734 if (!referenceObject) {
735 QString error = QStringLiteral("Cannot create binding on nested value type property");
737 v4->throwError(e);
738 return false;
739 }
740
744
746
751
753
758 if (f->isBoundFunction())
763 return true;
764 } else if (referenceObject) {
769 binding && !binding->isSticky()) {
771 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
772 const auto stackFrame = v4->currentStackFrame;
774 "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
779 }
780 }
783 }
784 }
785
788 if (value.isUndefined() && pd.isResettable()) {
789 property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
790 if (heapObject)
791 r->d()->writeBack(pd.coreIndex());
792 return true;
793 }
794
796
798 v = v.toInt();
799
800 void *gadget = r->d()->gadgetPtr();
801 const QMetaType type = v.metaType();
803 const QString error = QLatin1String("Cannot assign ") +
805 QLatin1String(" to ") +
808 return true;
809 }
810
811 if (heapObject)
812 r->d()->writeBack(pd.coreIndex());
813
814 return true;
815}
816
817} // namespace QV4
818
819QT_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