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
qqmlvmemetaobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 BasysKom GmbH.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7#include <private/qqmlrefcount_p.h>
9#include <qqmlinfo.h>
10
11#include <private/qqmlglobal_p.h>
12
13#include <private/qqmlpropertycachecreator_p.h>
14#include <private/qqmlpropertycachemethodarguments_p.h>
15#include <private/qqmlvaluetypewrapper_p.h>
16#include <private/qv4functionobject_p.h>
17#include <private/qv4jscall_p.h>
18#include <private/qv4object_p.h>
19#include <private/qv4qobjectwrapper_p.h>
20#include <private/qv4runtime_p.h>
21#include <private/qv4scopedvalue_p.h>
22#include <private/qv4sequenceobject_p.h>
23#include <private/qv4variantassociationobject_p.h>
24#include <private/qv4variantobject_p.h>
25
26#include <QtCore/qsequentialiterable.h>
27
28#include <climits> // for CHAR_BIT
29
30QT_BEGIN_NAMESPACE
31
32QQmlVMEResolvedList::QQmlVMEResolvedList(QQmlListProperty<QObject> *prop)
33{
34 // see QQmlVMEMetaObject::metaCall for how this was constructed
35 auto encodedIndex = quintptr(prop->data);
36 constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
37 quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
38 m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
39
40 // walk up to the correct meta object if necessary
41 auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
42 while (inheritanceDepth--)
43 mo = mo->parentVMEMetaObject();
44 m_metaObject = mo;
45 Q_ASSERT(m_metaObject);
46 Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)
47 ->property(m_metaObject->propOffset() + m_id)
48 .typeName(),
49 "QQmlListProperty"));
50 Q_ASSERT(m_metaObject->object == prop->object);
51
52 // readPropertyAsList() with checks transformed into Q_ASSERT
53 // and without allocation.
54 if (m_metaObject->propertyAndMethodStorage.isUndefined()
55 && m_metaObject->propertyAndMethodStorage.valueRef()) {
56 return;
57 }
58
59 if (auto *md = static_cast<QV4::MemberData *>(
60 m_metaObject->propertyAndMethodStorage.asManaged())) {
61 const QV4::Value *v = md->data() + m_id;
62 Q_ASSERT(v->as<QV4::Object>());
63 m_list = static_cast<QV4::Heap::Object *>(v->heapObject());
64 Q_ASSERT(m_list);
65 }
66}
67
68void QQmlVMEResolvedList::append(QObject *o) const
69{
70 QV4::Scope scope(m_list->internalClass->engine);
71 QV4::Heap::ArrayData *arrayData = m_list->arrayData;
72
73 const uint length = arrayData->length();
74 if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
75 scope.engine->throwRangeError(QLatin1String("Too many elements."));
76 return;
77 }
78
79 QV4::ScopedObject object(scope, m_list);
80 QV4::ArrayData::realloc(object, QV4::Heap::ArrayData::Simple, length + 1, false);
81 QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, o));
82 arrayData->vtable()->put(
83 object, length, wrappedObject);
84}
85
86QObject *QQmlVMEResolvedList::at(qsizetype i) const
87{
88 QV4::Scope scope(m_list->internalClass->engine);
89 QV4::Scoped<QV4::QObjectWrapper> result(scope, m_list->arrayData->get(i));
90 return result ? result->object() : nullptr;
91}
92
93void QQmlVMEResolvedList::replace(qsizetype i, QObject *o) const
94{
95 QV4::Scope scope(m_list->internalClass->engine);
96 QV4::ScopedObject object(scope, m_list);
97 QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, o));
98 m_list->arrayData->vtable()->put(object, i, wrappedObject);
99}
100
102
104{
105 m_metaObject->activate(m_metaObject->object, int(m_id), nullptr);
106}
107
108void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o)
109{
110 const QQmlVMEResolvedList resolved(prop);
111 resolved.append(o);
112 resolved.activateSignal();
113}
114
115void QQmlVMEMetaObject::list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o)
116{
117 QQmlVMEResolvedList(prop).append(o);
118}
119
120static qsizetype list_count(QQmlListProperty<QObject> *prop)
121{
122 return QQmlVMEResolvedList(prop).size();
123}
124
125static QObject *list_at(QQmlListProperty<QObject> *prop, qsizetype index)
126{
127 return QQmlVMEResolvedList(prop).at(index);
128}
129
130void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop)
131{
132 const QQmlVMEResolvedList resolved(prop);
133 resolved.clear();
134 resolved.activateSignal();
135}
136
137void QQmlVMEMetaObject::list_clear_nosignal(QQmlListProperty<QObject> *prop)
138{
139 QQmlVMEResolvedList(prop).clear();
140}
141
142static void list_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *o)
143{
144 const QQmlVMEResolvedList resolved(prop);
145 resolved.replace(index, o);
146 resolved.activateSignal();
147}
148
149static void list_removeLast(QQmlListProperty<QObject> *prop)
150{
151 const QQmlVMEResolvedList resolved(prop);
152 resolved.removeLast();
153 resolved.activateSignal();
154}
155
160
161void QQmlVMEVariantQObjectPtr::objectDestroyedImpl(QQmlGuardImpl *guard)
162{
163 auto This = static_cast<QQmlVMEVariantQObjectPtr *>(guard);
164 if (!This->m_target || QQmlData::wasDeleted(This->m_target->object))
165 return;
166
167 if (This->m_index >= 0) {
168 QV4::ExecutionEngine *v4 = This->m_target->propertyAndMethodStorage.engine();
169 if (v4) {
170 QV4::Scope scope(v4);
171 QV4::Scoped<QV4::MemberData> sp(scope, This->m_target->propertyAndMethodStorage.value());
172 if (sp) {
173 QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + This->m_index };
174 index.set(v4, QV4::Value::nullValue());
175 }
176 }
177
178 This->m_target->activate(This->m_target->object, This->m_index, nullptr);
179 }
180}
181
182void QQmlVMEVariantQObjectPtr::setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index)
183{
184 m_target = target;
185 m_index = index;
186 setObject(obj);
187}
188
202
207
208void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **)
209{
211 vmee->tryConnect();
212}
213
215{
216 int aliasId = this - metaObject->aliasEndpoints;
217
218 if (metaObject.tag() == EndPointIsConnected) {
219 // This is actually notify. Aliases are after regular properties in the MetaObject.
220 // So add propCount() to get the signal index.
221 int sigIdx = aliasId + metaObject->propCount();
222 metaObject->activate(metaObject->object, sigIdx, nullptr);
223 } else if (const QV4::CompiledData::Object *compiledObject = metaObject->findCompiledObject()) {
224 const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
225 if (!aliasData->isObjectAlias()) {
226 QQmlRefPointer<QQmlContextData> ctxt = metaObject->ctxt;
227 QObject *target = ctxt->idValue(aliasData->targetObjectId());
228 if (!target)
229 return;
230
231 QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
232 int coreIndex = encodedIndex.coreIndex();
233 int valueTypeIndex = encodedIndex.valueTypeIndex();
234 const QQmlPropertyData *pd = QQmlData::ensurePropertyCache(target)->property(coreIndex);
235 if (pd && valueTypeIndex != -1 && !QQmlMetaType::valueType(pd->propType())) {
236 // deep alias
237 const QQmlPropertyCache::ConstPtr newPropertyCache
238 = QQmlMetaType::propertyCacheForType(pd->propType());
239 void *argv[1] = { &target };
240 QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
241 Q_ASSERT(newPropertyCache);
242 pd = newPropertyCache->property(valueTypeIndex);
243 }
244 if (!pd)
245 return;
246
247 if (pd->notifyIndex() != -1 && ctxt->engine())
248 connect(target, pd->notifyIndex(), ctxt->engine());
249 }
250
251 metaObject.setTag(EndPointIsConnected);
252 }
253}
254
255
256QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlPropertyCache::ConstPtr &cache)
257 : object(obj),
258 cache(cache)
259{
260 QObjectPrivate *op = QObjectPrivate::get(obj);
261
262 if (op->metaObject) {
263 parent = op->metaObject;
264 // Use the extra flag in QBiPointer to know if we can safely cast parent.asT1() to QQmlVMEMetaObject*
265 parent.setFlagValue(QQmlData::get(obj)->hasVMEMetaObject);
266 } else {
267 parent = obj->metaObject();
268 }
269
270 op->metaObject = this;
271 QQmlData::get(obj)->hasInterceptorMetaObject = true;
272}
273
274QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject()
275{
276
277}
278
279void QQmlInterceptorMetaObject::registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor)
280{
281 for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
282 if (Q_UNLIKELY(vi->m_propertyIndex.coreIndex() == index.coreIndex())) {
283 qWarning() << "Attempting to set another interceptor on "
284 << object->metaObject()->className() << "property"
285 << object->metaObject()->property(index.coreIndex()).name()
286 << "- unsupported";
287 }
288 }
289
290 interceptor->m_propertyIndex = index;
291 interceptor->m_next = interceptors;
292 interceptors = interceptor;
293}
294
295int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a)
296{
297 Q_ASSERT(o == object);
298 Q_UNUSED(o);
299
300 if (intercept(c, id, a))
301 return -1;
302 return object->qt_metacall(c, id, a);
303}
304
305bool QQmlInterceptorMetaObject::doIntercept(QMetaObject::Call c, int id, void **a)
306{
307 for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
308 if (vi->m_propertyIndex.coreIndex() != id)
309 continue;
310
311 const int valueIndex = vi->m_propertyIndex.valueTypeIndex();
312 const QQmlData *data = QQmlData::get(object);
313 const QMetaType metaType = data->propertyCache->property(id)->propType();
314
315 if (metaType.isValid()) {
316 if (valueIndex != -1 && c == QMetaObject::WriteProperty) {
317
318 // If we didn't intend to change the property this interceptor cares about,
319 // then don't bother intercepting it. There may be an animation running on
320 // the property. We shouldn't disturb it.
321 const int changedProperty
322 = (*static_cast<int *>(a[3]) & QQmlPropertyData::HasInternalIndex)
323 ? *static_cast<int *>(a[4])
324 : QV4::ReferenceObject::AllProperties;
325 if (changedProperty == QV4::ReferenceObject::AllProperties
326 || changedProperty == valueIndex) {
327 // TODO: handle intercepting bindable properties for value types?
328 QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
329 data->context->engine(), metaType);
330 Q_ASSERT(valueType);
331
332 //
333 // Consider the following case:
334 // color c = { 0.1, 0.2, 0.3 }
335 // interceptor exists on c.r
336 // write { 0.2, 0.4, 0.6 }
337 //
338 // The interceptor may choose not to update the r component at this
339 // point (for example, a behavior that creates an animation). But we
340 // need to ensure that the g and b components are updated correctly.
341 //
342 // So we need to perform a full write where the value type is:
343 // r = old value, g = new value, b = new value
344 //
345 // And then call the interceptor which may or may not write the
346 // new value to the r component.
347 //
348 // This will ensure that the other components don't contain stale data
349 // and any relevant signals are emitted.
350 //
351 // To achieve this:
352 // (1) Store the new value type as a whole (needed due to
353 // aliasing between a[0] and static storage in value type).
354 // (2) Read the entire existing value type from object -> valueType temp.
355 // (3) Read the previous value of the component being changed
356 // from the valueType temp.
357 // (4) Write the entire new value type into the temp.
358 // (5) Overwrite the component being changed with the old value.
359 // (6) Perform a full write to the value type (which may emit signals etc).
360 // (7) Issue the interceptor call with the new component value.
361 //
362
363 QMetaProperty valueProp = valueType->property(valueIndex);
364 QVariant newValue(metaType, a[0]);
365
366 valueType->read(object, id);
367 QVariant prevComponentValue = valueType->readOnGadget(valueProp);
368
369 valueType->setValue(newValue);
370 QVariant newComponentValue = valueType->readOnGadget(valueProp);
371
372 // If the intercepted value seemingly has not changed, we still need to
373 // invoke the interceptor. There may be a pending animation that will
374 // change the value soon. Such an animation needs to be canceled if the
375 // current value is explicitly set.
376 // So, we cannot return here if prevComponentValue == newComponentValue.
377 valueType->writeOnGadget(valueProp, std::move(prevComponentValue));
378 valueType->write(
379 object, id,
380 QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor,
381 QV4::ReferenceObject::AllProperties);
382
383 vi->write(newComponentValue);
384 return true;
385 }
386 } else if (c == QMetaObject::WriteProperty) {
387 vi->write(QVariant(metaType, a[0]));
388 return true;
389 } else {
390 object->qt_metacall(c, id, a);
391 QUntypedBindable target = *reinterpret_cast<QUntypedBindable *>(a[0]);
392 return vi->bindable(reinterpret_cast<QUntypedBindable *>(a[0]), target);
393 }
394 }
395 }
396
397 return false;
398}
399
400static const QMetaObject *stringCastMetaObject(QObject *o, const QMetaObject *top)
401{
402 for (const QMetaObject *mo = top; mo; mo = mo->superClass()) {
403 if (o->qt_metacast(mo->className()) != nullptr)
404 return mo;
405 }
406 return nullptr;
407}
408
409#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
410const QMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObject *o) const
411#else
412QMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObject *o)
413#endif
414{
415 if (!metaObject)
416 metaObject = cache->createMetaObject();
417
418 const QMetaObject *mo = nullptr;
419 if (Q_UNLIKELY(metaObject.tag() == MetaObjectInvalid))
420 mo = stringCastMetaObject(o, metaObject->superClass());
421 if (!mo)
422 mo = metaObject.data();
423
424#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
425 return mo;
426#else
427 return const_cast<QMetaObject *>(mo);
428#endif
429}
430
431QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
432 QObject *obj,
433 const QQmlPropertyCache::ConstPtr &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId)
434 : QQmlInterceptorMetaObject(obj, cache),
435 engine(engine),
436 ctxt(QQmlData::get(obj, true)->outerContext),
437 aliasEndpoints(nullptr), compilationUnit(qmlCompilationUnit), qmlObjectId(qmlObjectId)
438{
439 Q_ASSERT(engine);
440 QQmlData::get(obj)->hasVMEMetaObject = true;
441
442 if (const QV4::CompiledData::Object *compiledObject = findCompiledObject()) {
443 numAliases = compiledObject->nAliases;
444 if (const uint size = compiledObject->nProperties + compiledObject->nFunctions) {
445 QV4::Heap::MemberData *data = QV4::MemberData::allocate(engine, size);
446 // we only have a weak reference below; if the VMEMetaObject is already marked
447 // (triggered by the allocate call above)
448 // we therefore might never mark the member data; consequently, mark it now
449 QV4::WriteBarrier::markCustom(engine, [data](QV4::MarkStack *ms) {
450 data->mark(ms);
451 });
452 propertyAndMethodStorage.set(engine, data);
453 std::fill(data->values.values, data->values.values + data->values.size, QV4::Encode::undefined());
454
455 // Need JS wrapper to ensure properties/methods are marked.
456 ensureQObjectWrapper();
457 }
458
459 // Counts retrieved from the property cache should reflect the CompiledObject
460 Q_ASSERT(propCount() == compiledObject->propertyCount());
461 Q_ASSERT(aliasCount() == compiledObject->aliasCount());
462 Q_ASSERT(signalCount() == compiledObject->signalCount()
463 + compiledObject->propertyCount()
464 + compiledObject->aliasCount());
465 Q_ASSERT(methodCount() == compiledObject->functionCount());
466 }
467
468 // We rely on ordering of properties, alias, signals, and methods.
469 Q_ASSERT(aliasOffset() == propOffset() + propCount());
470 Q_ASSERT(methodOffset() == signalOffset() + signalCount());
471}
472
473QQmlVMEMetaObject::~QQmlVMEMetaObject()
474{
475 if (parent.isT1()) parent.asT1()->objectDestroyed(object);
476 delete [] aliasEndpoints;
477
478 qDeleteAll(varObjectGuards);
479}
480
481QV4::MemberData *QQmlVMEMetaObject::propertyAndMethodStorageAsMemberData() const
482{
483 if (propertyAndMethodStorage.isUndefined()) {
484 if (propertyAndMethodStorage.valueRef())
485 // in some situations, the QObject wrapper (and associated data,
486 // such as the varProperties array) will have been cleaned up, but the
487 // QObject ptr will not yet have been deleted (eg, waiting on deleteLater).
488 // In this situation, return 0.
489 return nullptr;
490 }
491
492 return static_cast<QV4::MemberData*>(propertyAndMethodStorage.asManaged());
493}
494
495void QQmlVMEMetaObject::writeProperty(int id, int v)
496{
497 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
498 if (md)
499 md->set(engine, id, QV4::Value::fromInt32(v));
500}
501
502void QQmlVMEMetaObject::writeProperty(int id, bool v)
503{
504 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
505 if (md)
506 md->set(engine, id, QV4::Value::fromBoolean(v));
507}
508
509void QQmlVMEMetaObject::writeProperty(int id, double v)
510{
511 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
512 if (md)
513 md->set(engine, id, QV4::Value::fromDouble(v));
514}
515
516void QQmlVMEMetaObject::writeProperty(int id, const QString& v)
517{
518 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
519 if (md) {
520 QV4::Scope scope(engine);
521 QV4::Scoped<QV4::MemberData>(scope, md)->set(engine, id, engine->newString(v));
522 }
523}
524
525void QQmlVMEMetaObject::writeProperty(int id, QObject* v)
526{
527 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
528 if (md) {
529 QV4::Scope scope(engine);
530 QV4::Scoped<QV4::MemberData>(scope, md)->set(engine, id, QV4::Value::fromReturnedValue(
531 QV4::QObjectWrapper::wrap(engine, v)));
532 }
533
534 QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id);
535 if (v && !guard) {
536 guard = new QQmlVMEVariantQObjectPtr();
537 varObjectGuards.append(guard);
538 }
539 if (guard)
540 guard->setGuardedValue(v, this, id);
541}
542
543int QQmlVMEMetaObject::readPropertyAsInt(int id) const
544{
545 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
546 if (!md)
547 return 0;
548
549 QV4::Scope scope(engine);
550 QV4::ScopedValue sv(scope, *(md->data() + id));
551 if (!sv->isInt32())
552 return 0;
553 return sv->integerValue();
554}
555
556bool QQmlVMEMetaObject::readPropertyAsBool(int id) const
557{
558 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
559 if (!md)
560 return false;
561
562 QV4::Scope scope(engine);
563 QV4::ScopedValue sv(scope, *(md->data() + id));
564 if (!sv->isBoolean())
565 return false;
566 return sv->booleanValue();
567}
568
569double QQmlVMEMetaObject::readPropertyAsDouble(int id) const
570{
571 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
572 if (!md)
573 return 0.0;
574
575 QV4::Scope scope(engine);
576 QV4::ScopedValue sv(scope, *(md->data() + id));
577 if (!sv->isDouble())
578 return 0.0;
579 return sv->doubleValue();
580}
581
582QString QQmlVMEMetaObject::readPropertyAsString(int id) const
583{
584 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
585 if (!md)
586 return QString();
587
588 QV4::Scope scope(engine);
589 QV4::ScopedValue sv(scope, *(md->data() + id));
590 if (QV4::String *s = sv->stringValue())
591 return s->toQString();
592 return QString();
593}
594
595QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) const
596{
597 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
598 if (!md)
599 return QUrl();
600
601 QV4::Scope scope(engine);
602 QV4::ScopedValue sv(scope, *(md->data() + id));
603 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
604 if (!v || v->d()->data().userType() != QMetaType::QUrl)
605 return QUrl();
606 return v->d()->data().value<QUrl>();
607}
608
609QDate QQmlVMEMetaObject::readPropertyAsDate(int id) const
610{
611 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
612 if (!md)
613 return QDate();
614
615 QV4::Scope scope(engine);
616 QV4::ScopedValue sv(scope, *(md->data() + id));
617 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
618 if (!v || v->d()->data().userType() != QMetaType::QDate)
619 return QDate();
620 return v->d()->data().value<QDate>();
621}
622
623QTime QQmlVMEMetaObject::readPropertyAsTime(int id) const
624{
625 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
626 if (!md)
627 return QTime();
628
629 QV4::Scope scope(engine);
630 QV4::ScopedValue sv(scope, *(md->data() + id));
631 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
632 if (!v || v->d()->data().userType() != QMetaType::QTime)
633 return QTime();
634 return v->d()->data().value<QTime>();
635}
636
637QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) const
638{
639 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
640 if (!md)
641 return QDateTime();
642
643 QV4::Scope scope(engine);
644 QV4::ScopedValue sv(scope, *(md->data() + id));
645 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
646 if (!v || v->d()->data().userType() != QMetaType::QDateTime)
647 return QDateTime();
648 return v->d()->data().value<QDateTime>();
649}
650
651#if QT_CONFIG(regularexpression)
652QRegularExpression QQmlVMEMetaObject::readPropertyAsRegularExpression(int id) const
653{
654 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
655 if (!md)
656 return QRegularExpression();
657
658 QV4::Scope scope(engine);
659 QV4::ScopedValue sv(scope, *(md->data() + id));
660 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
661 if (!v || v->d()->data().userType() != QMetaType::QRegularExpression)
662 return QRegularExpression();
663 return v->d()->data().value<QRegularExpression>();
664}
665#endif
666
667QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const
668{
669 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
670 if (!md)
671 return QSizeF();
672
673 QV4::Scope scope(engine);
674 QV4::ScopedValue sv(scope, *(md->data() + id));
675 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
676 if (!v || v->d()->data().userType() != QMetaType::QSizeF)
677 return QSizeF();
678 return v->d()->data().value<QSizeF>();
679}
680
681QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) const
682{
683 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
684 if (!md)
685 return QPointF();
686
687 QV4::Scope scope(engine);
688 QV4::ScopedValue sv(scope, *(md->data() + id));
689 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
690 if (!v || v->d()->data().userType() != QMetaType::QPointF)
691 return QPointF();
692 return v->d()->data().value<QPointF>();
693}
694
695QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const
696{
697 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
698 if (!md)
699 return nullptr;
700
701 QV4::Scope scope(engine);
702 QV4::ScopedValue sv(scope, *(md->data() + id));
703 const QV4::QObjectWrapper *wrapper = sv->as<QV4::QObjectWrapper>();
704 if (!wrapper)
705 return nullptr;
706 return wrapper->object();
707}
708
709void QQmlVMEMetaObject::initPropertyAsList(int id) const
710{
711 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
712 if (!md)
713 return;
714
715 QV4::Scope scope(engine);
716 QV4::ScopedObject v(scope, *(md->data() + id));
717 if (!v) {
718 v = engine->newObject();
719 v->arrayCreate();
720 md->set(engine, id, v);
721 }
722}
723
724QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const
725{
726 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
727 if (!md)
728 return QRectF();
729
730 QV4::Scope scope(engine);
731 QV4::ScopedValue sv(scope, *(md->data() + id));
732 const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
733 if (!v || v->d()->data().userType() != QMetaType::QRectF)
734 return QRectF();
735 return v->d()->data().value<QRectF>();
736}
737
738int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void **a)
739{
740 Q_ASSERT(o == object);
741 Q_UNUSED(o);
742
743 int id = _id;
744
745 if (intercept(c, _id, a))
746 return -1;
747
748 if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty || c == QMetaObject::BindableProperty) {
749 if (id >= propOffset()) {
750 id -= propOffset();
751
752 const QQmlPropertyData *propertyData = cache->property(_id);
753 if (!propertyData->isAlias()) {
754
755 // the context can be null if accessing var properties from cpp after re-parenting an item.
756 QQmlEnginePrivate *ep = (ctxt.isNull() || ctxt->engine() == nullptr)
757 ? nullptr
758 : QQmlEnginePrivate::get(ctxt->engine());
759
760 if (c == QMetaObject::ReadProperty) {
761 if (propertyData->isQList()) {
762 // _id because this is an absolute property ID.
763 const QQmlPropertyData *propertyData = cache->property(_id);
764 const QMetaType propType = propertyData->propType();
765
766 if (propType.flags().testFlag(QMetaType::IsQmlList)) {
767 // when reading from the list, we need to find the correct MetaObject,
768 // namely this. However, obejct->metaObject might point to any
769 // MetaObject down the inheritance hierarchy, so we need to store how
770 // far we have to go down
771 // To do this, we encode the hierarchy depth together with the id of the
772 // property in a single quintptr, with the first half storing the depth
773 // and the second half storing the property id
774 auto mo = static_cast<QQmlVMEMetaObject *>(
775 QObjectPrivate::get(object)->metaObject);
776 quintptr inheritanceDepth = 0u;
777 while (mo && mo != this) {
778 mo = mo->parentVMEMetaObject();
779 ++inheritanceDepth;
780 }
781 constexpr quintptr idBits = sizeof(quintptr) * CHAR_BIT / 2u;
782 if (Q_UNLIKELY(inheritanceDepth >= (quintptr(1) << idBits))) {
783 qmlWarning(object) << "Too many objects in inheritance hierarchy "
784 "for list property";
785 return -1;
786 }
787 if (Q_UNLIKELY(quintptr(id) >= (quintptr(1) << idBits))) {
788 qmlWarning(object) << "Too many properties in object "
789 "for list property";
790 return -1;
791 }
792 quintptr encodedIndex = (inheritanceDepth << idBits) + id;
793
794 initPropertyAsList(id);
795 *static_cast<QQmlListProperty<QObject> *>(a[0])
796 = QQmlListProperty<QObject>(
797 object, reinterpret_cast<void *>(quintptr(encodedIndex)),
798 list_append, list_count, list_at,
799 list_clear, list_replace, list_removeLast);
800 } else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
801 // Value type list
802 QV4::Scope scope(engine);
803 QV4::Scoped<QV4::Sequence> sequence(scope, *(md->data() + id));
804 const void *data = sequence
805 ? QV4::SequencePrototype::getRawContainerPtr(sequence, propType)
806 : nullptr;
807 propType.destruct(a[0]);
808 propType.construct(a[0], data);
809 } else {
810 qmlWarning(object) << "Cannot find member data";
811 }
812 } else {
813 const QV4::CompiledData::CommonType t
814 = QQmlPropertyCacheCreatorBase::propertyTypeForMetaType(
815 propertyData->propType());
816 switch (t) {
817 case QV4::CompiledData::CommonType::Void:
818 break;
819 case QV4::CompiledData::CommonType::Int:
820 *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
821 break;
822 case QV4::CompiledData::CommonType::Bool:
823 *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
824 break;
825 case QV4::CompiledData::CommonType::Real:
826 *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
827 break;
828 case QV4::CompiledData::CommonType::String:
829 *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
830 break;
831 case QV4::CompiledData::CommonType::Url:
832 *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
833 break;
834 case QV4::CompiledData::CommonType::Date:
835 *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
836 break;
837 case QV4::CompiledData::CommonType::DateTime:
838 *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
839 break;
840 case QV4::CompiledData::CommonType::RegExp:
841#if QT_CONFIG(regularexpression)
842 *reinterpret_cast<QRegularExpression *>(a[0])
843 = readPropertyAsRegularExpression(id);
844#endif
845 break;
846 case QV4::CompiledData::CommonType::Rect:
847 *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
848 break;
849 case QV4::CompiledData::CommonType::Size:
850 *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
851 break;
852 case QV4::CompiledData::CommonType::Point:
853 *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
854 break;
855 case QV4::CompiledData::CommonType::Time:
856 *reinterpret_cast<QTime *>(a[0]) = readPropertyAsTime(id);
857 break;
858 case QV4::CompiledData::CommonType::Var:
859 if (ep) {
860 *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
861 } else {
862 // if the context was disposed,
863 // we just return an invalid variant from read.
864 *reinterpret_cast<QVariant *>(a[0]) = QVariant();
865 }
866 break;
867 case QV4::CompiledData::CommonType::Invalid:
868 if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
869 QV4::Scope scope(engine);
870 QV4::ScopedValue sv(scope, *(md->data() + id));
871
872 // _id because this is an absolute property ID.
873 const QQmlPropertyData *propertyData = cache->property(_id);
874
875 if (propertyData->isQObject()) {
876 if (const auto *wrap = sv->as<QV4::QObjectWrapper>())
877 *reinterpret_cast<QObject **>(a[0]) = wrap->object();
878 else
879 *reinterpret_cast<QObject **>(a[0]) = nullptr;
880 } else {
881 const QMetaType propType = propertyData->propType();
882 const void *data = nullptr;
883 if (const auto *v = sv->as<QV4::VariantObject>()) {
884 const QVariant &variant = v->d()->data();
885 if (variant.metaType() == propType)
886 data = variant.constData();
887 }
888 propType.destruct(a[0]);
889 propType.construct(a[0], data);
890 }
891 } else {
892 qmlWarning(object) << "Cannot find member data";
893 }
894 }
895 }
896 } else if (c == QMetaObject::WriteProperty) {
897 bool needActivate = false;
898
899 if (propertyData->isQList()) {
900 const QMetaType propType = propertyData->propType();
901
902 if (propType.flags().testFlag(QMetaType::IsQmlList)) {
903 // Writing such a property is not supported. Content is added through
904 // the list property methods.
905 } else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
906 // Value type list
907 QV4::Scope scope(engine);
908 QV4::Scoped<QV4::Sequence> sequence(scope, *(md->data() + id));
909 void *data = sequence
910 ? QV4::SequencePrototype::getRawContainerPtr(sequence, propType)
911 : nullptr;
912 if (data) {
913 if (!propType.equals(data, a[0])) {
914 propType.destruct(data);
915 propType.construct(data, a[0]);
916 needActivate = true;
917 }
918 } else {
919 if (const QQmlType type = QQmlMetaType::qmlListType(propType);
920 type.isSequentialContainer()) {
921 sequence = QV4::SequencePrototype::fromData(
922 engine, propType, type.listMetaSequence(), a[0]);
923 } else if (QSequentialIterable iterable;
924 QMetaType::convert(
925 propType, a[0],
926 QMetaType::fromType<QSequentialIterable>(),
927 &iterable)) {
928 sequence = QV4::SequencePrototype::fromData(
929 engine, propType, iterable.metaContainer(), a[0]);
930 } else {
931 sequence = QV4::Encode::undefined();
932 }
933 md->set(engine, id, sequence);
934 if (sequence->isUndefined()) {
935 qmlWarning(object)
936 << "Could not create a QML sequence object for "
937 << propType.name();
938 }
939 needActivate = true;
940 }
941 } else {
942 qmlWarning(object) << "Cannot find member data";
943 }
944 } else {
945 const QV4::CompiledData::CommonType t
946 = QQmlPropertyCacheCreatorBase::propertyTypeForMetaType(
947 propertyData->propType());
948 switch (t) {
949 case QV4::CompiledData::CommonType::Void:
950 break;
951 case QV4::CompiledData::CommonType::Int:
952 needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
953 writeProperty(id, *reinterpret_cast<int *>(a[0]));
954 break;
955 case QV4::CompiledData::CommonType::Bool:
956 needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
957 writeProperty(id, *reinterpret_cast<bool *>(a[0]));
958 break;
959 case QV4::CompiledData::CommonType::Real:
960 needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
961 writeProperty(id, *reinterpret_cast<double *>(a[0]));
962 break;
963 case QV4::CompiledData::CommonType::String:
964 needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
965 writeProperty(id, *reinterpret_cast<QString *>(a[0]));
966 break;
967 case QV4::CompiledData::CommonType::Url:
968 needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
969 writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
970 break;
971 case QV4::CompiledData::CommonType::Date:
972 needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
973 writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
974 break;
975 case QV4::CompiledData::CommonType::DateTime:
976 needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
977 writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
978 break;
979 case QV4::CompiledData::CommonType::RegExp:
980#if QT_CONFIG(regularexpression)
981 needActivate = *reinterpret_cast<QRegularExpression *>(a[0])
982 != readPropertyAsRegularExpression(id);
983 writeProperty(id, *reinterpret_cast<QRegularExpression *>(a[0]));
984#endif
985 break;
986 case QV4::CompiledData::CommonType::Rect:
987 needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
988 writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
989 break;
990 case QV4::CompiledData::CommonType::Size:
991 needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
992 writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
993 break;
994 case QV4::CompiledData::CommonType::Point:
995 needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
996 writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
997 break;
998 case QV4::CompiledData::CommonType::Time:
999 needActivate = *reinterpret_cast<QTime *>(a[0]) != readPropertyAsTime(id);
1000 writeProperty(id, *reinterpret_cast<QTime *>(a[0]));
1001 break;
1002 case QV4::CompiledData::CommonType::Var:
1003 if (ep)
1004 writeKnownVarProperty(id, *reinterpret_cast<QVariant *>(a[0]));
1005 break;
1006 case QV4::CompiledData::CommonType::Invalid:
1007 if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
1008 QV4::Scope scope(engine);
1009 QV4::ScopedValue sv(scope, *(md->data() + id));
1010
1011 // _id because this is an absolute property ID.
1012 const QQmlPropertyData *propertyData = cache->property(_id);
1013
1014 if (propertyData->isQObject()) {
1015 QObject *arg = *reinterpret_cast<QObject **>(a[0]);
1016 if (const auto *wrap = sv->as<QV4::QObjectWrapper>())
1017 needActivate = wrap->object() != arg;
1018 else if (arg != nullptr || !sv->isNull())
1019 needActivate = true;
1020 if (needActivate)
1021 writeProperty(id, arg);
1022 } else {
1023 const QMetaType propType = propertyData->propType();
1024 if (const auto *v = sv->as<QV4::VariantObject>()) {
1025 QVariant &variant = v->d()->data();
1026 if (variant.metaType() != propType) {
1027 needActivate = true;
1028 variant = QVariant(propType, a[0]);
1029 } else if (!propType.equals(variant.constData(), a[0])) {
1030 needActivate = true;
1031 propType.destruct(variant.data());
1032 propType.construct(variant.data(), a[0]);
1033 }
1034 } else {
1035 needActivate = true;
1036 md->set(engine, id, engine->newVariantObject(
1037 propType, a[0]));
1038 }
1039 }
1040 } else {
1041 qmlWarning(object) << "Cannot find member data";
1042 }
1043 }
1044 }
1045
1046 if (needActivate)
1047 activate(object, id, nullptr);
1048 }
1049
1050 return -1;
1051 }
1052
1053 id -= propCount();
1054
1055 if (id < aliasCount()) {
1056 const QV4::CompiledData::Object *compiledObject = findCompiledObject();
1057 if (!compiledObject)
1058 return -1;
1059
1060 const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id];
1061
1062 if (aliasData->hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject)
1063 && c == QMetaObject::ReadProperty){
1064 *reinterpret_cast<void **>(a[0]) = nullptr;
1065 }
1066
1067 if (ctxt.isNull())
1068 return -1;
1069
1070 while (aliasData->isAliasToLocalAlias())
1071 aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
1072
1073 QObject *target = ctxt->idValue(aliasData->targetObjectId());
1074 if (!target)
1075 return -1;
1076
1077 connectAlias(compiledObject, id);
1078
1079 if (aliasData->isObjectAlias()) {
1080 *reinterpret_cast<QObject **>(a[0]) = target;
1081 return -1;
1082 }
1083
1084 QQmlData *targetDData = QQmlData::get(target, /*create*/false);
1085 if (!targetDData)
1086 return -1;
1087
1088 QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
1089 int coreIndex = encodedIndex.coreIndex();
1090 const int valueTypePropertyIndex = encodedIndex.valueTypeIndex();
1091
1092 const auto removePendingBinding
1093 = [c, a](QObject *target, int coreIndex, QQmlPropertyIndex encodedIndex) {
1094 // Remove binding (if any) on write
1095 if (c == QMetaObject::WriteProperty) {
1096 int flags = *reinterpret_cast<int*>(a[3]);
1097 if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
1098 QQmlData *targetData = QQmlData::get(target);
1099 if (targetData && targetData->hasBindingBit(coreIndex)) {
1100 QQmlPropertyPrivate::removeBinding(target, encodedIndex);
1101 targetData->clearBindingBit(coreIndex);
1102 }
1103 }
1104 }
1105 };
1106
1107 if (valueTypePropertyIndex != -1) {
1108 if (!targetDData->propertyCache)
1109 return -1;
1110 const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
1111 // Value type property or deep alias
1112 QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
1113 ctxt->engine(), pd->propType());
1114 if (valueType) {
1115 removePendingBinding(target, coreIndex, encodedIndex);
1116 valueType->read(target, coreIndex);
1117 int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
1118
1119 if (c == QMetaObject::WriteProperty)
1120 valueType->write(target, coreIndex, QQmlPropertyData::HasInternalIndex,
1121 valueTypePropertyIndex);
1122
1123 return rv;
1124 } else {
1125 // deep alias
1126 void *argv[1] = { &target };
1127 QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
1128 removePendingBinding(
1129 target, valueTypePropertyIndex,
1130 QQmlPropertyIndex(valueTypePropertyIndex));
1131 return QMetaObject::metacall(target, c, valueTypePropertyIndex, a);
1132 }
1133
1134 } else {
1135 removePendingBinding(target, coreIndex, encodedIndex);
1136 return QMetaObject::metacall(target, c, coreIndex, a);
1137 }
1138
1139 }
1140 return -1;
1141
1142 }
1143
1144 } else if(c == QMetaObject::InvokeMetaMethod) {
1145
1146 if (id >= signalOffset()) {
1147
1148 id -= signalOffset();
1149 if (id < signalCount()) {
1150 activate(object, id, a);
1151 return -1;
1152 }
1153
1154 id -= signalCount();
1155 if (id < methodCount()) {
1156 QQmlEngine *engine = ctxt->engine();
1157 if (!engine)
1158 return -1; // We can't run the method
1159
1160 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1161 QV4::ExecutionEngine *v4 = engine->handle();
1162 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
1163 QV4::Scope scope(v4);
1164
1165
1166 QV4::Scoped<QV4::JavaScriptFunctionObject> function(scope, method(id));
1167 if (!function) {
1168 // The function was not compiled. There are some exceptional cases which the
1169 // expression rewriter does not rewrite properly (e.g., \r-terminated lines
1170 // are not rewritten correctly but this bug is deemed out-of-scope to fix for
1171 // performance reasons; see QTBUG-24064) and thus compilation will have failed.
1172 QQmlError e;
1173 e.setDescription(
1174 QStringLiteral(
1175 "Exception occurred during compilation of function: ")
1176 + QString::fromUtf8(metaObject->method(_id).methodSignature()));
1177 ep->warning(e);
1178 return -1; // The dynamic method with that id is not available.
1179 }
1180
1181 auto methodData = cache->method(_id);
1182 auto arguments = methodData->hasArguments() ? methodData->arguments() : nullptr;
1183
1184 if (arguments && arguments->names) {
1185 const quint32 parameterCount = arguments->names->size();
1186 Q_ASSERT(parameterCount == function->formalParameterCount());
1187 function->call(object, a, arguments->types, parameterCount);
1188 } else {
1189 Q_ASSERT(function->formalParameterCount() == 0);
1190 const QMetaType returnType = methodData->propType();
1191 function->call(object, a, &returnType, 0);
1192 }
1193
1194 if (scope.hasException()) {
1195 QQmlError error = scope.engine->catchExceptionAsQmlError();
1196 if (error.isValid())
1197 ep->warning(error);
1198 }
1199
1200 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
1201 return -1;
1202 }
1203 return -1;
1204 }
1205 }
1206
1207 if (parent.isT1())
1208 return parent.asT1()->metaCall(object, c, _id, a);
1209 else
1210 return object->qt_metacall(c, _id, a);
1211}
1212
1213QV4::ReturnedValue QQmlVMEMetaObject::method(int localMethodIndex) const
1214{
1215 if (ctxt.isNull() || !ctxt->isValid()) {
1216 qWarning("QQmlVMEMetaObject: Internal error - attempted to evaluate a function in an invalid context");
1217 return QV4::Encode::undefined();
1218 }
1219
1220 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
1221 if (!md)
1222 return QV4::Encode::undefined();
1223
1224 return (md->data() + localMethodIndex + propCount())->asReturnedValue();
1225}
1226
1227QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const
1228{
1229 Q_ASSERT(!findCompiledObject() || findCompiledObject()->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
1230
1231 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
1232 if (md)
1233 return (md->data() + id)->asReturnedValue();
1234 return QV4::Value::undefinedValue().asReturnedValue();
1235}
1236
1237QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const
1238{
1239 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
1240 if (md) {
1241 const QV4::QObjectWrapper *wrapper = (md->data() + id)->as<QV4::QObjectWrapper>();
1242 if (wrapper)
1243 return QVariant::fromValue(wrapper->object());
1244 const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>();
1245 if (v)
1246 return v->d()->data();
1247 return QV4::ExecutionEngine::toVariant(*(md->data() + id), QMetaType {});
1248 }
1249 return QVariant();
1250}
1251
1252void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
1253{
1254 Q_ASSERT(!findCompiledObject() || findCompiledObject()->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
1255
1256 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
1257 if (!md)
1258 return;
1259
1260 const QV4::Value &oldValue = (*md)[id];
1261 if (QV4::RuntimeHelpers::strictEqual(oldValue, value))
1262 return;
1263
1264 // Importantly, if the current value is a scarce resource, we need to ensure that it
1265 // gets automatically released by the engine if no other references to it exist.
1266 const QV4::VariantObject *oldVariant = oldValue.as<QV4::VariantObject>();
1267 if (oldVariant)
1268 oldVariant->removeVmePropertyReference();
1269
1270 QObject *valueObject = nullptr;
1271 QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id);
1272
1273 // And, if the new value is a scarce resource, we need to ensure that it does not get
1274 // automatically released by the engine until no other references to it exist.
1275 if (QV4::VariantObject *v = const_cast<QV4::VariantObject*>(value.as<QV4::VariantObject>())) {
1276 v->addVmePropertyReference();
1277 md->set(engine, id, value);
1278 } else if (QV4::QObjectWrapper *wrapper = const_cast<QV4::QObjectWrapper*>(value.as<QV4::QObjectWrapper>())) {
1279 // We need to track this QObject to signal its deletion
1280 valueObject = wrapper->object();
1281
1282 // Do we already have a QObject guard for this property?
1283 if (valueObject && !guard) {
1284 guard = new QQmlVMEVariantQObjectPtr();
1285 varObjectGuards.append(guard);
1286 }
1287 md->set(engine, id, value);
1288 } else if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) {
1289 md->set(engine, id, QV4::ReferenceObject::detached(sequence->d()));
1290 } else if (const QV4::QQmlValueTypeWrapper *wrapper = value.as<QV4::QQmlValueTypeWrapper>()) {
1291 md->set(engine, id, QV4::ReferenceObject::detached(wrapper->d()));
1292 } else if (const QV4::DateObject *date = value.as<QV4::DateObject>()) {
1293 md->set(engine, id, QV4::ReferenceObject::detached(date->d()));
1294 } else if (const QV4::VariantAssociationObject *association = value.as<QV4::VariantAssociationObject>()) {
1295 md->set(engine, id, QV4::ReferenceObject::detached(association->d()));
1296 } else {
1297 // TODO: We should have a virtualDetach to reduce the
1298 // boilerplate and avoid having to add new cases when a new
1299 // reference object is added.
1300 Q_ASSERT(!value.as<QV4::ReferenceObject>());
1301 md->set(engine, id, value);
1302 }
1303
1304 if (guard)
1305 guard->setGuardedValue(valueObject, this, id);
1306
1307 // Emit change signal as appropriate.
1308 activate(object, id, nullptr);
1309}
1310
1311void QQmlVMEMetaObject::writeKnownVarProperty(int id, const QVariant &value)
1312{
1313 // This can only be called from QQmlVMEMetaObject::metaCall() if we've already determined
1314 // that the property type is "var". No need to double-check it here.
1315 Q_ASSERT(findCompiledObject() && findCompiledObject()->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var);
1316
1317 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
1318 if (!md)
1319 return;
1320
1321 // Importantly, if the current value is a scarce resource, we need to ensure that it
1322 // gets automatically released by the engine if no other references to it exist.
1323 const QV4::VariantObject *oldv = (md->data() + id)->as<QV4::VariantObject>();
1324 if (oldv)
1325 oldv->removeVmePropertyReference();
1326
1327 // And, if the new value is a scarce resource, we need to ensure that it does not get
1328 // automatically released by the engine until no other references to it exist.
1329 QV4::Scope scope(engine);
1330 QV4::ScopedValue newv(scope, engine->fromVariant(value));
1331 QV4::Scoped<QV4::VariantObject> v(scope, newv);
1332 if (!!v)
1333 v->addVmePropertyReference();
1334
1335 // Write the value and emit change signal as appropriate.
1336 QVariant currentValue = readPropertyAsVariant(id);
1337 md->set(engine, id, newv);
1338 if ((currentValue.userType() != value.userType() || currentValue != value))
1339 activate(object, id, nullptr);
1340}
1341
1342QV4::ReturnedValue QQmlVMEMetaObject::vmeMethod(int index) const
1343{
1344 if (index < signalOffset()) {
1345 Q_ASSERT(parentVMEMetaObject());
1346 return parentVMEMetaObject()->vmeMethod(index);
1347 }
1348
1349 Q_ASSERT(index >= methodOffset());
1350 return method(index - methodOffset());
1351}
1352
1353// Used by debugger
1354void QQmlVMEMetaObject::setVmeMethod(int index, const QV4::Value &function)
1355{
1356 if (index < signalOffset()) {
1357 Q_ASSERT(parentVMEMetaObject());
1358 return parentVMEMetaObject()->setVmeMethod(index, function);
1359 }
1360
1361 Q_ASSERT(index >= methodOffset());
1362
1363 const int localMethodIndex = index - methodOffset();
1364 QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
1365 if (!md)
1366 return;
1367 md->set(engine, localMethodIndex + propCount(), function);
1368}
1369
1370QV4::ReturnedValue QQmlVMEMetaObject::vmeProperty(int index) const
1371{
1372 if (index < propOffset()) {
1373 Q_ASSERT(parentVMEMetaObject());
1374 return parentVMEMetaObject()->vmeProperty(index);
1375 }
1376 return readVarProperty(index - propOffset());
1377}
1378
1379void QQmlVMEMetaObject::setVMEProperty(int index, const QV4::Value &v)
1380{
1381 if (index < propOffset()) {
1382 Q_ASSERT(parentVMEMetaObject());
1383 parentVMEMetaObject()->setVMEProperty(index, v);
1384 return;
1385 }
1386 return writeVarProperty(index - propOffset(), v);
1387}
1388
1389void QQmlVMEMetaObject::ensureQObjectWrapper()
1390{
1391 Q_ASSERT(cache);
1392 QV4::QObjectWrapper::ensureWrapper(engine, object);
1393}
1394
1395void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack)
1396{
1397 if (engine != markStack->engine())
1398 return;
1399
1400 propertyAndMethodStorage.markOnce(markStack);
1401
1402 if (QQmlVMEMetaObject *parent = parentVMEMetaObject())
1403 parent->mark(markStack);
1404}
1405
1406bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const
1407{
1408 *target = nullptr;
1409 *coreIndex = -1;
1410 *valueTypeIndex = -1;
1411
1412 if (ctxt.isNull())
1413 return false;
1414
1415 const QV4::CompiledData::Object *compiledObject = findCompiledObject();
1416 if (!compiledObject)
1417 return false;
1418
1419 const int aliasId = index - propOffset() - compiledObject->nProperties;
1420 Q_ASSERT(index >= propOffset() + int(compiledObject->nProperties));
1421
1422 const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
1423 while (aliasData->isAliasToLocalAlias())
1424 aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
1425 *target = ctxt->idValue(aliasData->targetObjectId());
1426 if (!*target)
1427 return false;
1428
1429 if (!aliasData->isObjectAlias()) {
1430 QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
1431 *coreIndex = encodedIndex.coreIndex();
1432 *valueTypeIndex = encodedIndex.valueTypeIndex();
1433 }
1434 return true;
1435}
1436
1437void QQmlVMEMetaObject::connectAlias(const QV4::CompiledData::Object *compiledObject, int aliasId)
1438{
1439 Q_ASSERT(compiledObject);
1440 if (!aliasEndpoints)
1441 aliasEndpoints = new QQmlVMEMetaObjectEndpoint[compiledObject->nAliases];
1442
1443 const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
1444
1445 QQmlVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId;
1446 if (endpoint->metaObject.data()) {
1447 // already connected
1448 Q_ASSERT(endpoint->metaObject.data() == this);
1449 return;
1450 }
1451
1452 endpoint->metaObject = this;
1453 endpoint->connect(ctxt->idValueBindings(aliasData->targetObjectId()));
1454 endpoint->tryConnect();
1455}
1456
1457void QQmlVMEMetaObject::connectAliasSignal(int index, bool indexInSignalRange)
1458{
1459 if (const QV4::CompiledData::Object *compiledObject = findCompiledObject()) {
1460 const int aliasId = index
1461 - (indexInSignalRange ? cache->signalOffset() : signalOffset())
1462 - compiledObject->nProperties;
1463 if (aliasId < 0 || aliasId >= int(compiledObject->nAliases))
1464 return;
1465
1466 connectAlias(compiledObject, aliasId);
1467 }
1468}
1469
1470/*! \internal
1471 \a index is in the local signal index.
1472*/
1473void QQmlVMEMetaObject::activate(QObject *object, int index, void **args)
1474{
1475 QMetaObject::activate(object, cache->signalOffset(), index, args);
1476}
1477
1478QQmlVMEMetaObject *QQmlVMEMetaObject::getForProperty(QObject *o, int coreIndex)
1479{
1480 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
1481 while (vme && vme->cache->propertyOffset() > coreIndex)
1482 vme = vme->parentVMEMetaObject();
1483
1484 Q_ASSERT(vme);
1485 return vme;
1486}
1487
1488QQmlVMEMetaObject *QQmlVMEMetaObject::getForMethod(QObject *o, int coreIndex)
1489{
1490 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
1491 while (vme && vme->cache->methodOffset() > coreIndex)
1492 vme = vme->parentVMEMetaObject();
1493
1494 Q_ASSERT(vme);
1495 return vme;
1496}
1497
1498/*! \internal
1499 \a coreIndex is in the signal index range (see QObjectPrivate::signalIndex()).
1500 This is different from QMetaMethod::methodIndex().
1501*/
1502QQmlVMEMetaObject *QQmlVMEMetaObject::getForSignal(QObject *o, int coreIndex)
1503{
1504 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
1505 while (vme && vme->cache->signalOffset() > coreIndex)
1506 vme = vme->parentVMEMetaObject();
1507
1508 Q_ASSERT(vme);
1509 return vme;
1510}
1511
1512QQmlVMEVariantQObjectPtr *QQmlVMEMetaObject::getQObjectGuardForProperty(int index) const
1513{
1514 QList<QQmlVMEVariantQObjectPtr *>::ConstIterator it = varObjectGuards.constBegin(), end = varObjectGuards.constEnd();
1515 for ( ; it != end; ++it) {
1516 if ((*it)->m_index == index) {
1517 return *it;
1518 }
1519 }
1520
1521 return nullptr;
1522}
1523
1524QT_END_NAMESPACE
QTaggedPointer< QQmlVMEMetaObject, Tag > metaObject
void replace(qsizetype i, QObject *o) const
void append(QObject *o) const
QObject * at(qsizetype i) const
QQmlVMEMetaObject * m_target
void setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index)
static const QMetaObject * stringCastMetaObject(QObject *o, const QMetaObject *top)
static void list_removeLast(QQmlListProperty< QObject > *prop)
static QObject * list_at(QQmlListProperty< QObject > *prop, qsizetype index)
static qsizetype list_count(QQmlListProperty< QObject > *prop)
static void list_replace(QQmlListProperty< QObject > *prop, qsizetype index, QObject *o)
void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **)