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