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