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
5
6#include <private/qqmlbinding_p.h>
7#include <private/qqmlbuiltinfunctions_p.h>
8#include <private/qqmlvaluetype_p.h>
9
10#include <private/qv4alloca_p.h>
11#include <private/qv4arraybuffer_p.h>
12#include <private/qv4dateobject_p.h>
13#include <private/qv4engine_p.h>
14#include <private/qv4functionobject_p.h>
15#include <private/qv4identifiertable_p.h>
16#include <private/qv4jsonobject_p.h>
17#include <private/qv4lookup_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19#include <private/qv4stackframe_p.h>
20#include <private/qv4variantobject_p.h>
21
22#include <QtCore/qline.h>
23#include <QtCore/qsize.h>
24#include <QtCore/qdatetime.h>
25#include <QtCore/qloggingcategory.h>
26
27#if QT_CONFIG(regularexpression)
28#include <private/qv4regexpobject_p.h>
29#endif
30
32
33DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper);
34
35namespace QV4 {
36
38{
41}
42
44{
47}
48
54
56{
58
61 // This is a stale VariantReference. That is, the variant has been
62 // overwritten with a different type in the meantime.
63 // We need to modify this reference to the updated value type, if
64 // possible, or return false if it is not a value type.
68 setGadgetPtr(nullptr);
71 if (!mo)
72 return false;
73 } else {
74 return false;
75 }
76 }
77
79 return true;
80}
81
83{
84 if (!gadgetPtr()) {
85 setGadgetPtr(metaType().create(nullptr));
86 }
87 return gadgetPtr();
88}
89
91{
92 // If locations are enforced we only read once
94}
95
97{
99}
100
103{
106
107 // Either we're enforcing the location, then we have to read right away.
108 // Or we don't then we lazy-load. In neither case we pass any data.
110 nullptr, cloneFrom->metaType(), cloneFrom->metaObject(),
115 return r->asReturnedValue();
116}
117
128
130 Object *object, QMetaObject::Call call, int index, void **a)
131{
134
135 switch (call) {
140 if (wrapper->d()->object())
141 wrapper->d()->readReference();
142 break;
143 default:
144 break;
145 }
146
147 const QMetaObject *mo = wrapper->d()->metaObject();
148 if (!mo->d.static_metacall)
149 return 0;
150
151 mo->d.static_metacall(static_cast<QObject *>(wrapper->d()->gadgetPtr()), call, index, a);
152
153 switch (call) {
155 break;
158 if (wrapper->d()->object())
160 break;
163 if (wrapper->d()->object())
164 wrapper->d()->writeBack();
165 break;
166 default:
167 break;
168 }
169
170 return -1;
171}
172
176{
179
180 if (!type.isValid()) {
181 return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
183 }
184
185 // If data is given explicitly, we assume it has just been read from the property
190 if (!data && r->d()->enforcesLocation())
192 return r->asReturnedValue();
193}
194
197{
200
201 if (!type.isValid()) {
202 return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
204 }
205
208 data, type, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
209 return r->asReturnedValue();
210}
211
213{
214 if (d()->isReference() && !readReferenceValue())
215 return QVariant();
216 return d()->toVariant();
217}
218
220{
221 if (d()->isReference() && !readReferenceValue())
222 return false;
223 const QMetaType type = d()->metaType();
226 return true;
227}
228
230{
233
235 return lv->isEqual(rv->d()->data());
236
238 return lv->isEqual(v->toVariant());
239
240 return false;
241}
242
244{
245 if (!id.isString())
246 return Object::virtualHasProperty(m, id);
248 auto wrapper = static_cast<const QQmlValueTypeWrapper *>(m);
249 if (auto mo = wrapper->d()->metaObject())
250 if (mo->indexOfProperty(id.toQString().toUtf8()) != -1)
251 return true;
252
253 /* we don't want to fallback to QObject::virtualHasProperty
254 as that would end up calling getOwnProperty which is wasteful,
255 as it calls our own virtualGetOwnProperty.
256 As we know that our own properties are only those found on the meta-object,
257 we can instead skip the call, and simply check whether the property exists
258 on the prototype.
259 */
260 Scope scope(m->engine());
262 o = o->getPrototypeOf();
263 if (o)
264 return o->hasProperty(id);
265
266 return false;
267}
268
269static Heap::ReferenceObject::Flags referenceFlags(const QMetaObject *metaObject, int index)
270{
271 return metaObject->property(index).isWritable()
272 ? (Heap::ReferenceObject::CanWriteBack | Heap::ReferenceObject::EnforcesLocation)
273 : Heap::ReferenceObject::EnforcesLocation;
274}
275
277 const QMetaObject *metaObject, Heap::QQmlValueTypeWrapper *valueTypeWrapper,
278 int index, void **args)
279{
280 metaObject->d.static_metacall(
281 reinterpret_cast<QObject*>(
282 valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty, index, args);
283}
284
288{
289 if (isFunction) {
290 // calling a Q_INVOKABLE function of a value type
292 }
293
295 int index = coreIndex;
296
297 const auto wrapChar16 = [engine](char16_t c) {
298 return engine->newString(QChar(c));
299 };
300 const auto wrapQObject = [engine](QObject *object) {
302 };
303 const auto wrapJsonValue = [engine](const QJsonValue &value) {
305 };
306 const auto wrapJsonObject = [engine](const QJsonObject &object) {
308 };
309 const auto wrapJsonArray = [engine](const QJsonArray &array) {
311 };
312
313 const auto wrapQDateTime = [&](const QDateTime &dateTime) {
314 return engine->newDateObject(
316 };
317 const auto wrapQDate = [&](QDate date) {
318 return engine->newDateObject(
320 };
321 const auto wrapQTime = [&](QTime time) {
322 return engine->newDateObject(
324 };
325
326#define VALUE_TYPE_LOAD(metatype, cpptype, constructor)
327 case metatype: {
328 cpptype v;
329 void *args[] = { &v, nullptr };
330 doStaticReadCall(metaObject, valueTypeWrapper, index, args);
331 return QV4::Encode(constructor(v));
332 }
333
336
337 const int metaTypeId = isEnum
341 : metaType.id();
342
343 switch (metaTypeId) {
345 case QMetaType::Void:
346 return Encode::undefined();
347 case QMetaType::Nullptr:
348 case QMetaType::VoidStar:
349 return Encode::null();
350 VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
351 VALUE_TYPE_LOAD(QMetaType::Int, int, int);
353 VALUE_TYPE_LOAD(QMetaType::Long, long, double);
357 VALUE_TYPE_LOAD(QMetaType::Double, double, double);
360 VALUE_TYPE_LOAD(QMetaType::Float, float, float);
361 VALUE_TYPE_LOAD(QMetaType::Short, short, int);
362 VALUE_TYPE_LOAD(QMetaType::UShort, unsigned short, int);
363 VALUE_TYPE_LOAD(QMetaType::Char, char, int);
364 VALUE_TYPE_LOAD(QMetaType::UChar, unsigned char, int);
365 VALUE_TYPE_LOAD(QMetaType::SChar, signed char, int);
371#if QT_CONFIG(regularexpression)
373#endif
378 case QMetaType::QPixmap:
379 case QMetaType::QImage: {
381 void *args[] = { v.data(), nullptr };
384 }
385 case QMetaType::QVariant: {
386 QVariant v;
387 void *args[] = { &v, nullptr };
389 return engine->fromVariant(
392 }
393 default:
394 break;
395 }
396
398 void *args[] = { v.data(), nullptr };
401#undef VALUE_TYPE_LOAD
402}
403
405{
406 if (id.isString()) {
407 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
408 Q_ASSERT(r);
409
411 if (!result.isValid())
412 return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it.
413
414 if (!p)
415 return Attr_Data; // Property exists, but we're not interested in the value
416
417 if (!r->d()->isReference() || r->readReferenceValue()) {
418 // Property exists, and we can retrieve it
420 r->engine(), r->d(), result.propType(), result.coreIndex(),
422 } else {
423 // Property exists, but we can't retrieve it. Make it undefined.
424 p->value = Encode::undefined();
425 }
426
427 return Attr_Data;
428 }
429
430 return QV4::Object::virtualGetOwnProperty(m, id, p);
431}
432
434{
437 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
438
439};
440
441PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) {
442 const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o);
443
444 if (that->d()->isReference() && !that->readReferenceValue())
445 return PropertyKey::invalid();
446
447 const QMetaObject *mo = that->d()->metaObject();
448 // We don't return methods, ie. they are not visible when iterating
449 const int propertyCount = mo->propertyCount();
450 if (propertyIndex < propertyCount) {
451 Scope scope(that->engine());
452 QMetaProperty p = mo->property(propertyIndex); // TODO: Implement and use QBasicMetaProperty
453 ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(p.name())));
455 if (attrs)
456 *attrs = QV4::Attr_Data;
457 if (pd) {
458 QQmlPropertyData data;
459 data.load(p);
460 pd->value = QQmlValueTypeWrapper::getGadgetProperty(
461 that->engine(), that->d(), data.propType(), data.coreIndex(), data.isFunction(),
462 data.isEnum());
463 }
464 return propName->toPropertyKey();
465 }
466
467 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
468}
469
470
476
478{
479 if (d()->isReference() && !readReferenceValue())
480 return false;
481 int id1 = value.metaType().id();
482 QVariant v = d()->toVariant();
483 int id2 = v.metaType().id();
484 if (id1 != id2) {
485 // conversions for weak comparison
486 switch (id1) {
487 case QMetaType::QPoint:
488 if (id2 == QMetaType::QPointF)
489 return value.value<QPointF>() == v.value<QPointF>();
490 break;
491 case QMetaType::QPointF:
492 if (id2 == QMetaType::QPoint)
493 return value.value<QPointF>() == v.value<QPointF>();
494 break;
495 case QMetaType::QRect:
496 if (id2 == QMetaType::QRectF)
497 return value.value<QRectF>() == v.value<QRectF>();
498 break;
499 case QMetaType::QRectF:
500 if (id2 == QMetaType::QRect)
501 return value.value<QRectF>() == v.value<QRectF>();
502 break;
503 case QMetaType::QLine:
504 if (id2 == QMetaType::QLineF)
505 return value.value<QLineF>() == v.value<QLineF>();
506 break;
507 case QMetaType::QLineF:
508 if (id2 == QMetaType::QLine)
509 return value.value<QLineF>() == v.value<QLineF>();
510 break;
511 case QMetaType::QSize:
512 if (id2 == QMetaType::QSizeF)
513 return value.value<QSizeF>() == v.value<QSizeF>();
514 break;
515 case QMetaType::QSizeF:
516 if (id2 == QMetaType::QSize)
517 return value.value<QSizeF>() == v.value<QSizeF>();
518 break;
519 default:
520 break;
521 }
522 }
523 return (value == v);
524}
525
527{
528 return d()->metaType().id();
529}
530
532{
533 return d()->metaType();
534}
535
537{
538 bool destructGadgetOnExit = false;
539 auto cleanup = qScopeGuard([&]() {
541 d()->setDirty(true);
542 d()->metaType().destruct(d()->gadgetPtr());
543 d()->setGadgetPtr(nullptr);
544 }
545 });
546
547 if (d()->isReference()) {
549 QT_WARNING_DISABLE_GCC("-Walloca-larger-than="); // for size = alignment = 0
550 if (!d()->gadgetPtr()) {
551 const size_t size = d()->metaType().sizeOf();
552 const size_t alignment = d()->metaType().alignOf();
553 size_t space = size + alignment - 1;
554 Q_ALLOCA_VAR(void, rawPtr, space);
558 d()->metaType().construct(d()->gadgetPtr(), nullptr);
560 }
562 if (!readReferenceValue())
563 return false;
564 }
565
566 int flags = 0;
567 int status = -1;
568 void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
570 return true;
571}
572
590
592{
593 const Object *o = thisObject->as<Object>();
594 if (!o)
595 return b->engine()->throwTypeError();
597 if (!w)
598 return b->engine()->throwTypeError();
599
600 if (w->d()->isReference() && !w->readReferenceValue())
602
604 if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
606 result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
607 const QMetaObject *mo = w->d()->metaObject();
608 const int propCount = mo->propertyCount();
609 for (int i = 0; i < propCount; ++i) {
610 if (mo->property(i).isDesignable()) {
612 if (i > 0)
613 result += QLatin1String(", ");
614 result += value.toString();
615 }
616 }
617 result += QLatin1Char(')');
618 }
619 return Encode(b->engine()->newString(result));
620}
621
623 Lookup *lookup)
624{
627 if (!id.isString())
629
630 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
632 Scope scope(v4);
634
635 // Note: readReferenceValue() can change the reference->type.
636 if (r->d()->isReference() && !r->readReferenceValue())
638
640 if (!result.isValid())
642
644 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
651 return lookup->getter(engine, *object);
652}
653
659
661{
663
664 if (!id.isString())
666
667 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
669
670 // Note: readReferenceValue() can change the reference->type.
671 if (r->d()->isReference() && !r->readReferenceValue())
673
675 if (!result.isValid())
677
678 if (hasProperty)
679 *hasProperty = true;
680
682}
683
685{
686 if (!id.isString())
687 return Object::virtualPut(m, id, value, receiver);
688
690 ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
691 Scope scope(v4);
692 if (scope.hasException())
693 return false;
694
696 Heap::Object *heapObject = nullptr;
697 if (r->d()->isReference()) {
698 heapObject = r->d()->object();
699 if (!r->readReferenceValue() || !r->d()->canWriteBack())
700 return false;
701 }
702
703 const QMetaObject *metaObject = r->d()->metaObject();
705 if (!pd.isValid())
706 return false;
707
708 if (heapObject) {
709 QObject *referenceObject = nullptr;
711 const int referencePropertyIndex = r->d()->property();
713 if (o) {
715 } else {
717 if (t)
719 }
720
721 if (f) {
722 if (!f->isBinding()) {
723 // assigning a JS function to a non-var-property is not allowed.
724 QString error = QStringLiteral("Cannot assign JavaScript function to value-type property");
726 v4->throwError(e);
727 return false;
728 }
729
730 if (!referenceObject) {
731 QString error = QStringLiteral("Cannot create binding on nested value type property");
733 v4->throwError(e);
734 return false;
735 }
736
740
742
747
749
754 if (f->isBoundFunction())
759 return true;
760 } else if (referenceObject) {
765 binding && !binding->isSticky()) {
767 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
768 const auto stackFrame = v4->currentStackFrame;
770 "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
775 }
776 }
779 }
780 }
781
784 if (value.isUndefined() && pd.isResettable()) {
785 property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
786 if (heapObject)
787 r->d()->writeBack(pd.coreIndex());
788 return true;
789 }
790
792
794 v = v.toInt();
795
796 void *gadget = r->d()->gadgetPtr();
797 const QMetaType type = v.metaType();
799 const QString error = QLatin1String("Cannot assign ") +
801 QLatin1String(" to ") +
804 return true;
805 }
806
807 if (heapObject)
808 r->d()->writeBack(pd.coreIndex());
809
810 return true;
811}
812
813} // namespace QV4
814
815QT_END_NAMESPACE
Combined button and popup list for selecting options.
Definition qjsvalue.h:23
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