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