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

source code of qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp