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
4#include "qqmlvaluetypewrapper_p.h"
5
6#include <private/qqmlvaluetype_p.h>
7#include <private/qqmlbinding_p.h>
8#include <private/qqmlglobal_p.h>
9#include <private/qqmlbuiltinfunctions_p.h>
10
11#include <private/qv4engine_p.h>
12#include <private/qv4functionobject_p.h>
13#include <private/qv4variantobject_p.h>
14#include <private/qv4alloca_p.h>
15#include <private/qv4stackframe_p.h>
16#include <private/qv4objectiterator_p.h>
17#include <private/qv4qobjectwrapper_p.h>
18#include <private/qv4identifiertable_p.h>
19#include <private/qv4lookup_p.h>
20#include <private/qv4sequenceobject_p.h>
21#include <private/qv4arraybuffer_p.h>
22#include <private/qv4dateobject_p.h>
23#include <private/qv4jsonobject_p.h>
24#if QT_CONFIG(regularexpression)
25#include <private/qv4regexpobject_p.h>
26#endif
27#if QT_CONFIG(qml_locale)
28#include <private/qqmllocale_p.h>
29#endif
30#include <QtCore/qloggingcategory.h>
31#include <QtCore/qdatetime.h>
32#include <QtCore/QLine>
33#include <QtCore/QLineF>
34#include <QtCore/QSize>
35#include <QtCore/QSizeF>
36#include <QtCore/QTimeZone>
37
38QT_BEGIN_NAMESPACE
39
40Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval)
41
42DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper);
43
44namespace QV4 {
45
46Heap::QQmlValueTypeWrapper *Heap::QQmlValueTypeWrapper::detached() const
47{
48 return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>(
49 args: m_gadgetPtr, args: QMetaType(m_metaType), args: m_metaObject, args: nullptr, args: -1, args: NoFlag);
50}
51
52void Heap::QQmlValueTypeWrapper::destroy()
53{
54 if (m_gadgetPtr) {
55 metaType().destruct(data: m_gadgetPtr);
56 ::operator delete(m_gadgetPtr);
57 }
58 ReferenceObject::destroy();
59}
60
61void Heap::QQmlValueTypeWrapper::setData(const void *data)
62{
63 if (auto *gadget = gadgetPtr())
64 metaType().destruct(data: gadget);
65 if (!gadgetPtr())
66 setGadgetPtr(::operator new(metaType().sizeOf()));
67 metaType().construct(where: gadgetPtr(), copy: data);
68}
69
70QVariant Heap::QQmlValueTypeWrapper::toVariant() const
71{
72 Q_ASSERT(gadgetPtr());
73 return QVariant(metaType(), gadgetPtr());
74}
75
76bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
77{
78 Q_ASSERT(isVariant());
79
80 const QMetaType variantReferenceType = variant.metaType();
81 if (variantReferenceType != metaType()) {
82 // This is a stale VariantReference. That is, the variant has been
83 // overwritten with a different type in the meantime.
84 // We need to modify this reference to the updated value type, if
85 // possible, or return false if it is not a value type.
86 if (QQmlMetaType::isValueType(type: variantReferenceType)) {
87 const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type: variantReferenceType);
88 if (gadgetPtr()) {
89 metaType().destruct(data: gadgetPtr());
90 ::operator delete(gadgetPtr());
91 }
92 setGadgetPtr(nullptr);
93 setMetaObject(mo);
94 setMetaType(variantReferenceType);
95 if (!mo)
96 return false;
97 } else {
98 return false;
99 }
100 }
101
102 setData(variant.constData());
103 return true;
104}
105
106void *Heap::QQmlValueTypeWrapper::storagePointer()
107{
108 if (!gadgetPtr()) {
109 setGadgetPtr(::operator new(metaType().sizeOf()));
110 metaType().construct(where: gadgetPtr(), copy: nullptr);
111 }
112 return gadgetPtr();
113}
114
115bool Heap::QQmlValueTypeWrapper::readReference()
116{
117 // If locations are enforced we only read once
118 return enforcesLocation() || QV4::ReferenceObject::readReference(ref: this);
119}
120
121bool Heap::QQmlValueTypeWrapper::writeBack(int propertyIndex)
122{
123 return isAttachedToProperty() && QV4::ReferenceObject::writeBack(ref: this, internalIndex: propertyIndex);
124}
125
126ReturnedValue QQmlValueTypeWrapper::create(
127 ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, Heap::Object *object)
128{
129 QV4::Scope scope(engine);
130 initProto(v4: engine);
131
132 // Either we're enforcing the location, then we have to read right away.
133 // Or we don't then we lazy-load. In neither case we pass any data.
134 Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
135 args: nullptr, args: cloneFrom->metaType(), args: cloneFrom->metaObject(),
136 args&: object, args: cloneFrom->property(), args: cloneFrom->flags()));
137 r->d()->setLocation(function: cloneFrom->function(), statement: cloneFrom->statementIndex());
138 if (cloneFrom->enforcesLocation())
139 QV4::ReferenceObject::readReference(ref: r->d());
140 return r->asReturnedValue();
141}
142
143void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4)
144{
145 if (v4->valueTypeWrapperPrototype()->d_unchecked())
146 return;
147
148 Scope scope(v4);
149 ScopedObject o(scope, v4->newObject());
150 o->defineDefaultProperty(name: v4->id_toString(), code: method_toString, argumentCount: 1);
151 v4->jsObjects[QV4::ExecutionEngine::ValueTypeProto] = o->d();
152}
153
154int QQmlValueTypeWrapper::virtualMetacall(
155 Object *object, QMetaObject::Call call, int index, void **a)
156{
157 QQmlValueTypeWrapper *wrapper = object->as<QQmlValueTypeWrapper>();
158 Q_ASSERT(wrapper);
159
160 switch (call) {
161 case QMetaObject::InvokeMetaMethod:
162 case QMetaObject::ReadProperty:
163 case QMetaObject::BindableProperty:
164 case QMetaObject::CustomCall:
165 if (wrapper->d()->object())
166 wrapper->d()->readReference();
167 break;
168 default:
169 break;
170 }
171
172 const QMetaObject *mo = wrapper->d()->metaObject();
173 if (!mo->d.static_metacall)
174 return 0;
175
176 mo->d.static_metacall(static_cast<QObject *>(wrapper->d()->gadgetPtr()), call, index, a);
177
178 switch (call) {
179 case QMetaObject::ReadProperty:
180 break;
181 case QMetaObject::WriteProperty:
182 case QMetaObject::ResetProperty:
183 if (wrapper->d()->object())
184 wrapper->d()->writeBack(propertyIndex: index);
185 break;
186 case QMetaObject::InvokeMetaMethod:
187 case QMetaObject::CustomCall:
188 if (wrapper->d()->object())
189 wrapper->d()->writeBack();
190 break;
191 default:
192 break;
193 }
194
195 return -1;
196}
197
198ReturnedValue QQmlValueTypeWrapper::create(
199 ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type,
200 Heap::Object *object, int property, Heap::ReferenceObject::Flags flags)
201{
202 Scope scope(engine);
203 initProto(v4: engine);
204
205 if (!type.isValid()) {
206 return engine->throwTypeError(message: QLatin1String("Type %1 is not a value type")
207 .arg(args: QString::fromUtf8(utf8: type.name())));
208 }
209
210 // If data is given explicitly, we assume it has just been read from the property
211 Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
212 args&: data, args&: type, args&: metaObject, args&: object, args&: property, args&: flags));
213 if (CppStackFrame *frame = engine->currentStackFrame)
214 r->d()->setLocation(function: frame->v4Function, statement: frame->statementNumber());
215 if (!data && r->d()->enforcesLocation())
216 QV4::ReferenceObject::readReference(ref: r->d());
217 return r->asReturnedValue();
218}
219
220ReturnedValue QQmlValueTypeWrapper::create(
221 ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type)
222{
223 Scope scope(engine);
224 initProto(v4: engine);
225
226 if (!type.isValid()) {
227 return engine->throwTypeError(message: QLatin1String("Type %1 is not a value type")
228 .arg(args: QString::fromUtf8(utf8: type.name())));
229 }
230
231 Scoped<QQmlValueTypeWrapper> r(
232 scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
233 args&: data, args&: type, args&: metaObject, args: nullptr, args: -1, args: Heap::ReferenceObject::NoFlag));
234 return r->asReturnedValue();
235}
236
237QVariant QQmlValueTypeWrapper::toVariant() const
238{
239 if (d()->isReference() && !readReferenceValue())
240 return QVariant();
241 return d()->toVariant();
242}
243
244bool QQmlValueTypeWrapper::toGadget(void *data) const
245{
246 if (d()->isReference() && !readReferenceValue())
247 return false;
248 const QMetaType type = d()->metaType();
249 type.destruct(data);
250 type.construct(where: data, copy: d()->gadgetPtr());
251 return true;
252}
253
254bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other)
255{
256 Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other);
257 QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m);
258
259 if (QV4::VariantObject *rv = other->as<VariantObject>())
260 return lv->isEqual(value: rv->d()->data());
261
262 if (QV4::QQmlValueTypeWrapper *v = other->as<QQmlValueTypeWrapper>())
263 return lv->isEqual(value: v->toVariant());
264
265 return false;
266}
267
268bool QQmlValueTypeWrapper::virtualHasProperty(const Managed *m, PropertyKey id)
269{
270 if (!id.isString())
271 return Object::virtualHasProperty(m, id);
272 Q_ASSERT(m && m->as<QQmlValueTypeWrapper>());
273 auto wrapper = static_cast<const QQmlValueTypeWrapper *>(m);
274 if (auto mo = wrapper->d()->metaObject())
275 if (mo->indexOfProperty(name: id.toQString().toUtf8()) != -1)
276 return true;
277
278 /* we don't want to fallback to QObject::virtualHasProperty
279 as that would end up calling getOwnProperty which is wasteful,
280 as it calls our own virtualGetOwnProperty.
281 As we know that our own properties are only those found on the meta-object,
282 we can instead skip the call, and simply check whether the property exists
283 on the prototype.
284 */
285 Scope scope(m->engine());
286 ScopedObject o(scope, m);
287 o = o->getPrototypeOf();
288 if (o)
289 return o->hasProperty(id);
290
291 return false;
292}
293
294static Heap::ReferenceObject::Flags referenceFlags(const QMetaObject *metaObject, int index)
295{
296 return metaObject->property(index).isWritable()
297 ? (Heap::ReferenceObject::CanWriteBack | Heap::ReferenceObject::EnforcesLocation)
298 : Heap::ReferenceObject::EnforcesLocation;
299}
300
301static void doStaticReadCall(
302 const QMetaObject *metaObject, Heap::QQmlValueTypeWrapper *valueTypeWrapper,
303 int index, void **args)
304{
305 metaObject->d.static_metacall(
306 reinterpret_cast<QObject*>(
307 valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty, index, args);
308}
309
310static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
311 Heap::QQmlValueTypeWrapper *valueTypeWrapper,
312 QMetaType metaType, quint16 coreIndex, bool isFunction, bool isEnum)
313{
314 if (isFunction) {
315 // calling a Q_INVOKABLE function of a value type
316 return QV4::QObjectMethod::create(scope: engine->rootContext(), valueType: valueTypeWrapper, index: coreIndex);
317 }
318
319 const QMetaObject *metaObject = valueTypeWrapper->metaObject();
320 int index = coreIndex;
321
322 const auto wrapChar16 = [engine](char16_t c) {
323 return engine->newString(s: QChar(c));
324 };
325 const auto wrapQObject = [engine](QObject *object) {
326 return QObjectWrapper::wrap(engine, object);
327 };
328 const auto wrapJsonValue = [engine](const QJsonValue &value) {
329 return JsonObject::fromJsonValue(engine, value);
330 };
331 const auto wrapJsonObject = [engine](const QJsonObject &object) {
332 return JsonObject::fromJsonObject(engine, object);
333 };
334 const auto wrapJsonArray = [engine](const QJsonArray &array) {
335 return JsonObject::fromJsonArray(engine, array);
336 };
337
338 const auto wrapQDateTime = [&](const QDateTime &dateTime) {
339 return engine->newDateObject(
340 dateTime, parent: valueTypeWrapper, index, flags: referenceFlags(metaObject, index));
341 };
342 const auto wrapQDate = [&](QDate date) {
343 return engine->newDateObject(
344 date, parent: valueTypeWrapper, index, flags: referenceFlags(metaObject, index));
345 };
346 const auto wrapQTime = [&](QTime time) {
347 return engine->newDateObject(
348 time, parent: valueTypeWrapper, index, flags: referenceFlags(metaObject, index));
349 };
350
351#if QT_CONFIG(qml_locale)
352 const auto wrapLocale = [engine](const QLocale &locale) {
353 return QQmlLocale::wrap(engine, locale);
354 };
355#endif
356
357#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
358 case metatype: { \
359 cpptype v; \
360 void *args[] = { &v, nullptr }; \
361 doStaticReadCall(metaObject, valueTypeWrapper, index, args); \
362 return QV4::Encode(constructor(v)); \
363 }
364
365 QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(
366 type: QMetaObject::ReadProperty, metaObject: &metaObject, index: &index);
367
368 const int metaTypeId = isEnum
369 ? metaType.underlyingType().id()
370 : (metaType.flags() & QMetaType::PointerToQObject)
371 ? QMetaType::QObjectStar
372 : metaType.id();
373
374 switch (metaTypeId) {
375 case QMetaType::UnknownType:
376 case QMetaType::Void:
377 return Encode::undefined();
378 case QMetaType::Nullptr:
379 case QMetaType::VoidStar:
380 return Encode::null();
381 VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
382 VALUE_TYPE_LOAD(QMetaType::Int, int, int);
383 VALUE_TYPE_LOAD(QMetaType::UInt, uint, uint);
384 VALUE_TYPE_LOAD(QMetaType::Long, long, double);
385 VALUE_TYPE_LOAD(QMetaType::ULong, ulong, double);
386 VALUE_TYPE_LOAD(QMetaType::LongLong, qlonglong, double);
387 VALUE_TYPE_LOAD(QMetaType::ULongLong, qulonglong, double);
388 VALUE_TYPE_LOAD(QMetaType::Double, double, double);
389 VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
390 VALUE_TYPE_LOAD(QMetaType::QByteArray, QByteArray, engine->newArrayBuffer);
391 VALUE_TYPE_LOAD(QMetaType::Float, float, float);
392 VALUE_TYPE_LOAD(QMetaType::Short, short, int);
393 VALUE_TYPE_LOAD(QMetaType::UShort, unsigned short, int);
394 VALUE_TYPE_LOAD(QMetaType::Char, char, int);
395 VALUE_TYPE_LOAD(QMetaType::UChar, unsigned char, int);
396 VALUE_TYPE_LOAD(QMetaType::SChar, signed char, int);
397 VALUE_TYPE_LOAD(QMetaType::QChar, QChar, engine->newString);
398 VALUE_TYPE_LOAD(QMetaType::Char16, char16_t, wrapChar16);
399 VALUE_TYPE_LOAD(QMetaType::QDateTime, QDateTime, wrapQDateTime);
400 VALUE_TYPE_LOAD(QMetaType::QDate, QDate, wrapQDate);
401 VALUE_TYPE_LOAD(QMetaType::QTime, QTime, wrapQTime);
402#if QT_CONFIG(regularexpression)
403 VALUE_TYPE_LOAD(QMetaType::QRegularExpression, QRegularExpression, engine->newRegExpObject);
404#endif
405 VALUE_TYPE_LOAD(QMetaType::QObjectStar, QObject*, wrapQObject);
406 VALUE_TYPE_LOAD(QMetaType::QJsonValue, QJsonValue, wrapJsonValue);
407 VALUE_TYPE_LOAD(QMetaType::QJsonObject, QJsonObject, wrapJsonObject);
408 VALUE_TYPE_LOAD(QMetaType::QJsonArray, QJsonArray, wrapJsonArray);
409#if QT_CONFIG(qml_locale)
410 VALUE_TYPE_LOAD(QMetaType::QLocale, QLocale, wrapLocale);
411#endif
412 case QMetaType::QPixmap:
413 case QMetaType::QImage: {
414 QVariant v(metaType);
415 void *args[] = { v.data(), nullptr };
416 doStaticReadCall(metaObject, valueTypeWrapper, index, args);
417 return Encode(engine->newVariantObject(type: metaType, data: v.data()));
418 }
419 case QMetaType::QVariant: {
420 QVariant v;
421 void *args[] = { &v, nullptr };
422 doStaticReadCall(metaObject, valueTypeWrapper, index, args);
423 return engine->fromVariant(
424 variant: v, parent: valueTypeWrapper, property: index,
425 flags: referenceFlags(metaObject, index) | Heap::ReferenceObject::IsVariant);
426 }
427 default:
428 break;
429 }
430
431 QVariant v(metaType);
432 void *args[] = { v.data(), nullptr };
433 doStaticReadCall(metaObject, valueTypeWrapper, index, args);
434 return engine->fromVariant(variant: v, parent: valueTypeWrapper, property: index, flags: referenceFlags(metaObject, index));
435#undef VALUE_TYPE_LOAD
436}
437
438PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
439{
440 if (id.isString()) {
441 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
442 Q_ASSERT(r);
443
444 const QQmlPropertyData result = r->dataForPropertyKey(id);
445 if (!result.isValid())
446 return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it.
447
448 if (!p)
449 return Attr_Data; // Property exists, but we're not interested in the value
450
451 if (!r->d()->isReference() || r->readReferenceValue()) {
452 // Property exists, and we can retrieve it
453 p->value = getGadgetProperty(
454 engine: r->engine(), valueTypeWrapper: r->d(), metaType: result.propType(), coreIndex: result.coreIndex(),
455 isFunction: result.isFunction(), isEnum: result.isEnum());
456 } else {
457 // Property exists, but we can't retrieve it. Make it undefined.
458 p->value = Encode::undefined();
459 }
460
461 return Attr_Data;
462 }
463
464 return QV4::Object::virtualGetOwnProperty(m, id, p);
465}
466
467struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
468{
469 int propertyIndex = 0;
470 ~QQmlValueTypeWrapperOwnPropertyKeyIterator() override = default;
471 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
472
473};
474
475PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) {
476 const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o);
477
478 if (that->d()->isReference() && !that->readReferenceValue())
479 return PropertyKey::invalid();
480
481 const QMetaObject *mo = that->d()->metaObject();
482 // We don't return methods, ie. they are not visible when iterating
483 const int propertyCount = mo->propertyCount();
484 if (propertyIndex < propertyCount) {
485 Scope scope(that->engine());
486 QMetaProperty p = mo->property(index: propertyIndex); // TODO: Implement and use QBasicMetaProperty
487 ScopedString propName(scope, that->engine()->newString(s: QString::fromUtf8(utf8: p.name())));
488 ++propertyIndex;
489 if (attrs)
490 *attrs = QV4::Attr_Data;
491 if (pd) {
492 QQmlPropertyData data;
493 data.load(p);
494 pd->value = getGadgetProperty(engine: that->engine(), valueTypeWrapper: that->d(), metaType: data.propType(), coreIndex: data.coreIndex(), isFunction: data.isFunction(), isEnum: data.isEnum());
495 }
496 return propName->toPropertyKey();
497 }
498
499 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
500}
501
502
503OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
504{
505 *target = *m;
506 return new QQmlValueTypeWrapperOwnPropertyKeyIterator;
507}
508
509bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
510{
511 if (d()->isReference() && !readReferenceValue())
512 return false;
513 int id1 = value.metaType().id();
514 QVariant v = d()->toVariant();
515 int id2 = v.metaType().id();
516 if (id1 != id2) {
517 // conversions for weak comparison
518 switch (id1) {
519 case QMetaType::QPoint:
520 if (id2 == QMetaType::QPointF)
521 return value.value<QPointF>() == v.value<QPointF>();
522 break;
523 case QMetaType::QPointF:
524 if (id2 == QMetaType::QPoint)
525 return value.value<QPointF>() == v.value<QPointF>();
526 break;
527 case QMetaType::QRect:
528 if (id2 == QMetaType::QRectF)
529 return value.value<QRectF>() == v.value<QRectF>();
530 break;
531 case QMetaType::QRectF:
532 if (id2 == QMetaType::QRect)
533 return value.value<QRectF>() == v.value<QRectF>();
534 break;
535 case QMetaType::QLine:
536 if (id2 == QMetaType::QLineF)
537 return value.value<QLineF>() == v.value<QLineF>();
538 break;
539 case QMetaType::QLineF:
540 if (id2 == QMetaType::QLine)
541 return value.value<QLineF>() == v.value<QLineF>();
542 break;
543 case QMetaType::QSize:
544 if (id2 == QMetaType::QSizeF)
545 return value.value<QSizeF>() == v.value<QSizeF>();
546 break;
547 case QMetaType::QSizeF:
548 if (id2 == QMetaType::QSize)
549 return value.value<QSizeF>() == v.value<QSizeF>();
550 break;
551 default:
552 break;
553 }
554 }
555 return (value == v);
556}
557
558int QQmlValueTypeWrapper::typeId() const
559{
560 return d()->metaType().id();
561}
562
563QMetaType QQmlValueTypeWrapper::type() const
564{
565 return d()->metaType();
566}
567
568bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
569{
570 bool destructGadgetOnExit = false;
571 Q_ALLOCA_DECLARE(void, gadget);
572 if (d()->isReference()) {
573 if (!d()->gadgetPtr()) {
574 Q_ALLOCA_ASSIGN(void, gadget, d()->metaType().sizeOf());
575 d()->setGadgetPtr(gadget);
576 d()->metaType().construct(where: d()->gadgetPtr(), copy: nullptr);
577 destructGadgetOnExit = true;
578 }
579 if (!readReferenceValue())
580 return false;
581 }
582
583 int flags = 0;
584 int status = -1;
585 void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
586 QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
587
588 if (destructGadgetOnExit) {
589 d()->metaType().destruct(data: d()->gadgetPtr());
590 d()->setGadgetPtr(nullptr);
591 }
592 return true;
593}
594
595QQmlPropertyData QQmlValueTypeWrapper::dataForPropertyKey(PropertyKey id) const
596{
597 if (!id.isStringOrSymbol())
598 return QQmlPropertyData {};
599 QByteArray name = id.asStringOrSymbol()->toQString().toUtf8();
600 const QMetaObject *mo = d()->metaObject();
601 QQmlPropertyData result;
602 QMetaMethod metaMethod = QMetaObjectPrivate::firstMethod(baseObject: mo, name);
603 if (metaMethod.isValid()) {
604 result.load(metaMethod);
605 } else {
606 int propertyIndex = d()->metaObject()->indexOfProperty(name: name.constData());
607 if (propertyIndex >= 0)
608 result.load(mo->property(index: propertyIndex));
609 }
610 return result;
611}
612
613ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
614{
615 const Object *o = thisObject->as<Object>();
616 if (!o)
617 return b->engine()->throwTypeError();
618 const QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>();
619 if (!w)
620 return b->engine()->throwTypeError();
621
622 if (w->d()->isReference() && !w->readReferenceValue())
623 RETURN_UNDEFINED();
624
625 QString result;
626 if (!QMetaType::convert(fromType: w->d()->metaType(), from: w->d()->gadgetPtr(),
627 toType: QMetaType(QMetaType::QString), to: &result)) {
628 result = QString::fromUtf8(utf8: w->d()->metaType().name()) + QLatin1Char('(');
629 const QMetaObject *mo = w->d()->metaObject();
630 const int propCount = mo->propertyCount();
631 for (int i = 0; i < propCount; ++i) {
632 if (mo->property(index: i).isDesignable()) {
633 QVariant value = mo->property(index: i).readOnGadget(gadget: w->d()->gadgetPtr());
634 if (i > 0)
635 result += QLatin1String(", ");
636 result += value.toString();
637 }
638 }
639 result += QLatin1Char(')');
640 }
641 return Encode(b->engine()->newString(s: result));
642}
643
644ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine,
645 Lookup *lookup)
646{
647 PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->
648 runtimeStrings[lookup->nameIndex]);
649 if (!id.isString())
650 return Object::virtualResolveLookupGetter(object, engine, lookup);
651
652 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
653 QV4::ExecutionEngine *v4 = r->engine();
654 Scope scope(v4);
655 ScopedString name(scope, id.asStringOrSymbol());
656
657 // Note: readReferenceValue() can change the reference->type.
658 if (r->d()->isReference() && !r->readReferenceValue())
659 return Value::undefinedValue().asReturnedValue();
660
661 QQmlPropertyData result = r->dataForPropertyKey(id);
662 if (!result.isValid())
663 return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
664
665 lookup->qgadgetLookup.ic = r->internalClass();
666 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
667 lookup->qgadgetLookup.metaObject = quintptr(r->d()->metaObject()) + 1;
668 lookup->qgadgetLookup.metaType = result.propType().iface();
669 lookup->qgadgetLookup.coreIndex = result.coreIndex();
670 lookup->qgadgetLookup.isFunction = result.isFunction();
671 lookup->qgadgetLookup.isEnum = result.isEnum();
672 lookup->getter = QQmlValueTypeWrapper::lookupGetter;
673 return lookup->getter(lookup, engine, *object);
674}
675
676ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
677{
678 const auto revertLookup = [lookup, engine, &object]() {
679 lookup->qgadgetLookup.metaObject = quintptr(0);
680 lookup->getter = Lookup::getterGeneric;
681 return Lookup::getterGeneric(l: lookup, engine, object);
682 };
683
684 // we can safely cast to a QV4::Object here. If object is something else,
685 // the internal class won't match
686 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
687 if (!o || o->internalClass != lookup->qgadgetLookup.ic)
688 return revertLookup();
689
690 Heap::QQmlValueTypeWrapper *valueTypeWrapper =
691 const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
692 if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1))
693 return revertLookup();
694
695 if (valueTypeWrapper->isReference() && !valueTypeWrapper->readReference())
696 return Encode::undefined();
697
698 return getGadgetProperty(
699 engine, valueTypeWrapper, metaType: QMetaType(lookup->qgadgetLookup.metaType),
700 coreIndex: lookup->qgadgetLookup.coreIndex, isFunction: lookup->qgadgetLookup.isFunction,
701 isEnum: lookup->qgadgetLookup.isEnum);
702}
703
704bool QQmlValueTypeWrapper::lookupSetter(
705 Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
706{
707 return QV4::Lookup::setterFallback(l, engine, object, value);
708}
709
710bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
711 const Value &value)
712{
713 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
714}
715
716ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
717{
718 Q_ASSERT(m->as<QQmlValueTypeWrapper>());
719
720 if (!id.isString())
721 return Object::virtualGet(m, id, receiver, hasProperty);
722
723 const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
724 QV4::ExecutionEngine *v4 = r->engine();
725
726 // Note: readReferenceValue() can change the reference->type.
727 if (r->d()->isReference() && !r->readReferenceValue())
728 return Value::undefinedValue().asReturnedValue();
729
730 QQmlPropertyData result = r->dataForPropertyKey(id);
731 if (!result.isValid())
732 return Object::virtualGet(m, id, receiver, hasProperty);
733
734 if (hasProperty)
735 *hasProperty = true;
736
737 return getGadgetProperty(engine: v4, valueTypeWrapper: r->d(), metaType: result.propType(), coreIndex: result.coreIndex(), isFunction: result.isFunction(), isEnum: result.isEnum());
738}
739
740bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
741{
742 if (!id.isString())
743 return Object::virtualPut(m, id, value, receiver);
744
745 Q_ASSERT(m->as<QQmlValueTypeWrapper>());
746 ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
747 Scope scope(v4);
748 if (scope.hasException())
749 return false;
750
751 Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m));
752 Heap::Object *heapObject = nullptr;
753 if (r->d()->isReference()) {
754 heapObject = r->d()->object();
755 if (!r->readReferenceValue() || !r->d()->canWriteBack())
756 return false;
757 }
758
759 const QMetaObject *metaObject = r->d()->metaObject();
760 const QQmlPropertyData pd = r->dataForPropertyKey(id);
761 if (!pd.isValid())
762 return false;
763
764 if (heapObject) {
765 QObject *referenceObject = nullptr;
766 QV4::ScopedFunctionObject f(scope, value);
767 const int referencePropertyIndex = r->d()->property();
768 QV4::Scoped<QV4::QObjectWrapper> o(scope, heapObject);
769 if (o) {
770 referenceObject = o->object();
771 } else {
772 QV4::Scoped<QV4::QQmlTypeWrapper> t(scope, heapObject);
773 if (t)
774 referenceObject = t->object();
775 }
776
777 if (f) {
778 if (!f->isBinding()) {
779 // assigning a JS function to a non-var-property is not allowed.
780 QString error = QStringLiteral("Cannot assign JavaScript function to value-type property");
781 ScopedString e(scope, v4->newString(s: error));
782 v4->throwError(value: e);
783 return false;
784 }
785
786 if (!referenceObject) {
787 QString error = QStringLiteral("Cannot create binding on nested value type property");
788 ScopedString e(scope, v4->newString(s: error));
789 v4->throwError(value: e);
790 return false;
791 }
792
793 const QMetaProperty writebackProperty
794 = referenceObject->metaObject()->property(index: referencePropertyIndex);
795 const QMetaType writeBackPropertyType = writebackProperty.metaType();
796
797 QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext();
798
799 QQmlPropertyData cacheData;
800 cacheData.setWritable(true);
801 cacheData.setPropType(writeBackPropertyType);
802 cacheData.setCoreIndex(referencePropertyIndex);
803
804 QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
805
806 QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
807 QV4::ScopedContext ctx(scope, f->scope());
808 QQmlBinding *newBinding = QQmlBinding::create(property: &cacheData, function: f->function(), obj: referenceObject, ctxt: context, scope: ctx);
809 newBinding->setSourceLocation(bindingFunction->currentLocation());
810 if (f->isBoundFunction())
811 newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
812 newBinding->setSourceLocation(bindingFunction->currentLocation());
813 newBinding->setTarget(referenceObject, cacheData, valueType: &pd);
814 QQmlPropertyPrivate::setBinding(binding: newBinding);
815 return true;
816 } else if (referenceObject) {
817 if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
818 if (auto binding = QQmlPropertyPrivate::binding(referenceObject, index: QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()))) {
819 Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding);
820 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
821 const auto stackFrame = v4->currentStackFrame;
822 qCInfo(lcBindingRemoval,
823 "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
824 referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(),
825 qPrintable(qmlBinding->expressionIdentifier()),
826 metaObject->property(pd.coreIndex()).name(),
827 qPrintable(stackFrame->source()), stackFrame->lineNumber());
828 }
829 }
830 QQmlPropertyPrivate::removeBinding(o: referenceObject, index: QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()));
831 }
832 }
833
834 QMetaProperty property = metaObject->property(index: pd.coreIndex());
835 Q_ASSERT(property.isValid());
836 if (value.isUndefined() && pd.isResettable()) {
837 property.resetOnGadget(gadget: reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
838 if (heapObject)
839 r->d()->writeBack(propertyIndex: pd.coreIndex());
840 return true;
841 }
842
843 QVariant v = QV4::ExecutionEngine::toVariant(value, typeHint: property.metaType());
844
845 if (property.isEnumType() && (QMetaType::Type)v.userType() == QMetaType::Double)
846 v = v.toInt();
847
848 void *gadget = r->d()->gadgetPtr();
849 property.writeOnGadget(gadget, value: std::move(v));
850
851 if (heapObject)
852 r->d()->writeBack(propertyIndex: pd.coreIndex());
853
854 return true;
855}
856
857} // namespace QV4
858
859QT_END_NAMESPACE
860

source code of qtdeclarative/src/qml/qml/qqmlvaluetypewrapper.cpp