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()->metaType().destruct(d()->gadgetPtr());
542 d()->setGadgetPtr(nullptr);
543 }
544 });
545
546 if (d()->isReference()) {
547 if (!d()->gadgetPtr()) {
548 const size_t size = d()->metaType().sizeOf();
549 const size_t alignment = d()->metaType().alignOf();
550 size_t space = size + alignment - 1;
551 Q_ALLOCA_VAR(void, rawPtr, space);
555 d()->metaType().construct(d()->gadgetPtr(), nullptr);
557 }
558 if (!readReferenceValue())
559 return false;
560 }
561
562 int flags = 0;
563 int status = -1;
564 void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
566 return true;
567}
568
586
588{
589 const Object *o = thisObject->as<Object>();
590 if (!o)
591 return b->engine()->throwTypeError();
593 if (!w)
594 return b->engine()->throwTypeError();
595
596 if (w->d()->isReference() && !w->readReferenceValue())
598
600 if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
602 result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
603 const QMetaObject *mo = w->d()->metaObject();
604 const int propCount = mo->propertyCount();
605 for (int i = 0; i < propCount; ++i) {
606 if (mo->property(i).isDesignable()) {
608 if (i > 0)
609 result += QLatin1String(", ");
610 result += value.toString();
611 }
612 }
613 result += QLatin1Char(')');
614 }
615 return Encode(b->engine()->newString(result));
616}
617
619 Lookup *lookup)
620{
623 if (!id.isString())
625
626 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
628 Scope scope(v4);
630
631 // Note: readReferenceValue() can change the reference->type.
632 if (r->d()->isReference() && !r->readReferenceValue())
634
636 if (!result.isValid())
638
640 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
647 return lookup->getter(engine, *object);
648}
649
655
657{
659
660 if (!id.isString())
662
663 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
665
666 // Note: readReferenceValue() can change the reference->type.
667 if (r->d()->isReference() && !r->readReferenceValue())
669
671 if (!result.isValid())
673
674 if (hasProperty)
675 *hasProperty = true;
676
678}
679
681{
682 if (!id.isString())
683 return Object::virtualPut(m, id, value, receiver);
684
686 ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
687 Scope scope(v4);
688 if (scope.hasException())
689 return false;
690
692 Heap::Object *heapObject = nullptr;
693 if (r->d()->isReference()) {
694 heapObject = r->d()->object();
695 if (!r->readReferenceValue() || !r->d()->canWriteBack())
696 return false;
697 }
698
699 const QMetaObject *metaObject = r->d()->metaObject();
701 if (!pd.isValid())
702 return false;
703
704 if (heapObject) {
705 QObject *referenceObject = nullptr;
707 const int referencePropertyIndex = r->d()->property();
709 if (o) {
711 } else {
713 if (t)
715 }
716
717 if (f) {
718 if (!f->isBinding()) {
719 // assigning a JS function to a non-var-property is not allowed.
720 QString error = QStringLiteral("Cannot assign JavaScript function to value-type property");
722 v4->throwError(e);
723 return false;
724 }
725
726 if (!referenceObject) {
727 QString error = QStringLiteral("Cannot create binding on nested value type property");
729 v4->throwError(e);
730 return false;
731 }
732
736
738
743
745
750 if (f->isBoundFunction())
755 return true;
756 } else if (referenceObject) {
761 binding && !binding->isSticky()) {
763 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
764 const auto stackFrame = v4->currentStackFrame;
766 "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
771 }
772 }
775 }
776 }
777
780 if (value.isUndefined() && pd.isResettable()) {
781 property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
782 if (heapObject)
783 r->d()->writeBack(pd.coreIndex());
784 return true;
785 }
786
788
790 v = v.toInt();
791
792 void *gadget = r->d()->gadgetPtr();
793 const QMetaType type = v.metaType();
795 const QString error = QLatin1String("Cannot assign ") +
797 QLatin1String(" to ") +
800 return true;
801 }
802
803 if (heapObject)
804 r->d()->writeBack(pd.coreIndex());
805
806 return true;
807}
808
809} // namespace QV4
810
811QT_END_NAMESPACE
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