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