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

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