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