Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlopenmetaobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6#include <private/qqmlpropertycache_p.h>
7#include <private/qqmldata_p.h>
8#include <private/qqmlmetatype_p.h>
9
10#include <private/qmetaobjectbuilder_p.h>
11#include <qdebug.h>
12#include <QtCore/qpointer.h>
13#include <QtCore/qset.h>
14
16
17
19{
20public:
22
23 void init(const QMetaObject *metaObj);
24
30
31 // TODO: We need to make sure that this does not escape into other threads.
32 // In particular, all its non-const uses are probably wrong. You should
33 // only set the open metaobject to "cached" once it's not going to be
34 // modified anymore.
36
38};
39
40QQmlOpenMetaObjectType::QQmlOpenMetaObjectType(const QMetaObject *base)
41 : d(new QQmlOpenMetaObjectTypePrivate)
42{
43 d->init(base);
44}
45
46QQmlOpenMetaObjectType::~QQmlOpenMetaObjectType()
47{
48 if (d->mem)
49 free(d->mem);
50 delete d;
51}
52
53int QQmlOpenMetaObjectType::propertyOffset() const
54{
55 return d->propertyOffset;
56}
57
58int QQmlOpenMetaObjectType::signalOffset() const
59{
60 return d->signalOffset;
61}
62
63int QQmlOpenMetaObjectType::propertyCount() const
64{
65 return d->names.size();
66}
67
68QByteArray QQmlOpenMetaObjectType::propertyName(int idx) const
69{
70 Q_ASSERT(idx >= 0 && idx < d->names.size());
71
72 return d->mob.property(idx).name();
73}
74
75QQmlPropertyCache::Ptr QQmlOpenMetaObjectType::cache() const
76{
77 return d->cache;
78}
79
80void QQmlOpenMetaObjectType::createProperties(const QList<QByteArray> &names)
81{
82 for (int i = 0; i < names.size(); ++i) {
83 const QByteArray &name = names.at(i);
84 const int id = d->mob.propertyCount();
85 d->mob.addSignal("__" + QByteArray::number(id) + "()");
86 QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id);
87 propertyCreated(id, build);
88 d->names.insert(name, id);
89 }
90 free(d->mem);
91 d->mem = d->mob.toMetaObject();
92 QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin();
93 while (it != d->referers.end()) {
94 QQmlOpenMetaObject *omo = *it;
95 *static_cast<QMetaObject *>(omo) = *d->mem;
96 if (d->cache)
97 d->cache->update(omo);
98 ++it;
99 }
100}
101
102int QQmlOpenMetaObjectType::createProperty(const QByteArray &name)
103{
104 const int signalIdx = d->mob.addSignal(
105 "__" + QByteArray::number(d->mob.propertyCount()) + "()").index();
106 QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", signalIdx);
107 propertyCreated(build.index(), build);
108 free(d->mem);
109 d->mem = d->mob.toMetaObject();
110 d->names.insert(name, build.index());
111 QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin();
112 while (it != d->referers.end()) {
113 QQmlOpenMetaObject *omo = *it;
114 *static_cast<QMetaObject *>(omo) = *d->mem;
115 if (d->cache)
116 d->cache->update(omo);
117 ++it;
118 }
119
120 return d->propertyOffset + build.index();
121}
122
123void QQmlOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder)
124{
125 if (d->referers.size())
126 (*d->referers.begin())->propertyCreated(id, builder);
127}
128
129void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj)
130{
131 if (!mem) {
132 mob.setSuperClass(metaObj);
133 mob.setClassName(metaObj->className());
134 mob.setFlags(MetaObjectFlag::DynamicMetaObject);
135
136 mem = mob.toMetaObject();
137
138 propertyOffset = mem->propertyOffset();
139 signalOffset = mem->methodOffset();
140 }
141}
142
143//----------------------------------------------------------------------------
144
146{
147public:
148 QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, QObject *obj)
149 : q(_q), object(obj) {}
150
151 struct Property {
152 private:
153 QVariant m_value;
154 QPointer<QObject> qobjectTracker;
155 public:
156 bool valueSet = false;
157
158 QVariant value() const {
159 if (m_value.metaType().flags() & QMetaType::PointerToQObject
160 && qobjectTracker.isNull())
161 return QVariant::fromValue<QObject*>(nullptr);
162 return m_value;
163 }
164 QVariant &valueRef() { return m_value; }
165 void setValue(const QVariant &v) {
166 m_value = v;
167 valueSet = true;
168 if (v.metaType().flags() & QMetaType::PointerToQObject)
169 qobjectTracker = m_value.value<QObject*>();
170 }
171 };
172
173 inline void setPropertyValue(int idx, const QVariant &value) {
174 if (data.size() <= idx)
175 data.resize(idx + 1);
176 data[idx].setValue(value);
177 }
178
179 inline Property &propertyRef(int idx) {
180 if (data.size() <= idx)
181 data.resize(idx + 1);
182 Property &prop = data[idx];
183 if (!prop.valueSet)
184 prop.setValue(q->initialValue(idx));
185 return prop;
186 }
187
188 inline QVariant propertyValue(int idx) {
189 auto &prop = propertyRef(idx);
190 return prop.value();
191 }
192
193 inline QVariant &propertyValueRef(int idx) {
194 auto &prop = propertyRef(idx);
195 return prop.valueRef();
196 }
197
198 inline bool hasProperty(int idx) const {
199 if (idx >= data.size())
200 return false;
201 return data[idx].valueSet;
202 }
203
205 // Do not drop the property cache if we've created it ourselves.
206 // In that case we've also updated it to reflect any changes.
207 if (cacheProperties)
208 return;
209 if (QQmlData *ddata = QQmlData::get(object, /*create*/false))
210 ddata->propertyCache.reset();
211 }
212
219 bool autoCreate = true;
220 bool cacheProperties = false;
221};
222
223QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base)
224: d(new QQmlOpenMetaObjectPrivate(this, obj))
225{
226 d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject()));
227 d->type->d->referers.insert(this);
228
229 QObjectPrivate *op = QObjectPrivate::get(obj);
230 d->parent = op->metaObject;
231 *static_cast<QMetaObject *>(this) = *d->type->d->mem;
232 op->metaObject = this;
233}
234
235QQmlOpenMetaObject::QQmlOpenMetaObject(
236 QObject *obj, const QQmlRefPointer<QQmlOpenMetaObjectType> &type)
237: d(new QQmlOpenMetaObjectPrivate(this, obj))
238{
239 d->type = type;
240 d->type->d->referers.insert(this);
241
242 QObjectPrivate *op = QObjectPrivate::get(obj);
243 d->parent = op->metaObject;
244 *static_cast<QMetaObject *>(this) = *d->type->d->mem;
245 op->metaObject = this;
246}
247
248QQmlOpenMetaObject::~QQmlOpenMetaObject()
249{
250 if (d->parent)
251 delete d->parent;
252 d->type->d->referers.remove(this);
253 delete d;
254}
255
256QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const
257{
258 return d->type.data();
259}
260
261void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName)
262{
263 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(propertyName);
264 if (iter == d->type->d->names.constEnd())
265 return;
266 activate(d->object, *iter + d->type->d->signalOffset, nullptr);
267}
268
269void QQmlOpenMetaObject::unparent()
270{
271 d->parent = nullptr;
272}
273
274int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a)
275{
276 Q_ASSERT(d->object == o);
277
278 if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty)
279 && id >= d->type->d->propertyOffset) {
280 int propId = id - d->type->d->propertyOffset;
281 if (c == QMetaObject::ReadProperty) {
282 propertyRead(propId);
283 *reinterpret_cast<QVariant *>(a[0]) = d->propertyValue(propId);
284 } else if (c == QMetaObject::WriteProperty) {
285 if (propId >= d->data.size() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) {
286 propertyWrite(propId);
287 d->setPropertyValue(propId, propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])));
288 propertyWritten(propId);
289 activate(o, d->type->d->signalOffset + propId, nullptr);
290 }
291 }
292 return -1;
293 } else {
294 if (d->parent)
295 return d->parent->metaCall(o, c, id, a);
296 else
297 return o->qt_metacall(c, id, a);
298 }
299}
300
301QDynamicMetaObjectData *QQmlOpenMetaObject::parent() const
302{
303 return d->parent;
304}
305
306bool QQmlOpenMetaObject::checkedSetValue(int index, const QVariant &value, bool force)
307{
308 if (!force && d->propertyValue(index) == value)
309 return false;
310
311 d->setPropertyValue(index, value);
312 activate(d->object, index + d->type->d->signalOffset, nullptr);
313 return true;
314}
315
316QVariant QQmlOpenMetaObject::value(int id) const
317{
318 return d->propertyValue(id);
319}
320
321void QQmlOpenMetaObject::setValue(int id, const QVariant &value)
322{
323 d->setPropertyValue(id, propertyWriteValue(id, value));
324 activate(d->object, id + d->type->d->signalOffset, nullptr);
325}
326
327QVariant QQmlOpenMetaObject::value(const QByteArray &name) const
328{
329 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
330 if (iter == d->type->d->names.cend())
331 return QVariant();
332
333 return d->propertyValue(*iter);
334}
335
336QVariant &QQmlOpenMetaObject::valueRef(const QByteArray &name)
337{
338 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
339 Q_ASSERT(iter != d->type->d->names.cend());
340
341 return d->propertyValueRef(*iter);
342}
343
344bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, bool force)
345{
346 QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
347
348 int id = -1;
349 if (iter == d->type->d->names.cend()) {
350 id = createProperty(name.constData(), "") - d->type->d->propertyOffset;
351 } else {
352 id = *iter;
353 }
354
355 if (id >= 0)
356 return checkedSetValue(id, val, force);
357
358 return false;
359}
360
361void QQmlOpenMetaObject::setValues(const QHash<QByteArray, QVariant> &values, bool force)
362{
363 QList<QByteArray> missingProperties;
364 d->deferredPropertyNames = &missingProperties;
365 const auto &names = d->type->d->names;
366
367 for (auto valueIt = values.begin(), end = values.end(); valueIt != end; ++valueIt) {
368 const auto nameIt = names.constFind(valueIt.key());
369 if (nameIt == names.constEnd()) {
370 const int id = createProperty(valueIt.key(), "") - d->type->d->propertyOffset;
371
372 // If id >= 0 some override of createProperty() created it. Then set it.
373 // Else it either ends up in missingProperties and we create it later
374 // or it cannot be created.
375
376 if (id >= 0)
377 checkedSetValue(id, valueIt.value(), force);
378 } else {
379 checkedSetValue(*nameIt, valueIt.value(), force);
380 }
381 }
382
383 d->deferredPropertyNames = nullptr;
384 if (missingProperties.isEmpty())
385 return;
386
387 d->type->createProperties(missingProperties);
388 d->dropStalePropertyCache();
389
390 for (const QByteArray &name : std::as_const(missingProperties))
391 checkedSetValue(names[name], values[name], force);
392}
393
394// returns true if this value has been initialized by a call to either value() or setValue()
395bool QQmlOpenMetaObject::hasValue(int id) const
396{
397 return d->hasProperty(id);
398}
399
400void QQmlOpenMetaObject::setCached(bool c)
401{
402 if (c == d->cacheProperties)
403 return;
404
405 d->cacheProperties = c;
406
407 QQmlData *qmldata = QQmlData::get(d->object, true);
408 if (d->cacheProperties) {
409 // As the propertyCache is not saved in QQmlMetaType (due to it being dynamic)
410 // we cannot leak it to other places before we're done with it. Yes, it's still
411 // terrible.
412 if (!d->type->d->cache)
413 d->type->d->cache = QQmlPropertyCache::createStandalone(this);
414 qmldata->propertyCache = d->type->d->cache;
415 } else {
416 d->type->d->cache.reset();
417 qmldata->propertyCache.reset();
418 }
419}
420
421bool QQmlOpenMetaObject::autoCreatesProperties() const
422{
423 return d->autoCreate;
424}
425
426void QQmlOpenMetaObject::setAutoCreatesProperties(bool autoCreate)
427{
428 d->autoCreate = autoCreate;
429}
430
431
432int QQmlOpenMetaObject::createProperty(const char *name, const char *)
433{
434 if (d->autoCreate) {
435 if (d->deferredPropertyNames) {
436 // Defer the creation of new properties. See setValues(QHash<QByteArray, QVariant>)
437 d->deferredPropertyNames->append(name);
438 return -1;
439 }
440
441 const int result = d->type->createProperty(name);
442 d->dropStalePropertyCache();
443 return result;
444 } else
445 return -1;
446}
447
448void QQmlOpenMetaObject::propertyRead(int)
449{
450}
451
452void QQmlOpenMetaObject::propertyWrite(int)
453{
454}
455
456QVariant QQmlOpenMetaObject::propertyWriteValue(int, const QVariant &value)
457{
458 return value;
459}
460
461void QQmlOpenMetaObject::propertyWritten(int)
462{
463}
464
465void QQmlOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &)
466{
467}
468
469QVariant QQmlOpenMetaObject::initialValue(int)
470{
471 return QVariant();
472}
473
474int QQmlOpenMetaObject::count() const
475{
476 return d->type->d->names.size();
477}
478
479QByteArray QQmlOpenMetaObject::name(int idx) const
480{
481 Q_ASSERT(idx >= 0 && idx < d->type->d->names.size());
482
483 return d->type->d->mob.property(idx).name();
484}
485
486QObject *QQmlOpenMetaObject::object() const
487{
488 return d->object;
489}
490
491QT_END_NAMESPACE
QList< QByteArray > * deferredPropertyNames
void setPropertyValue(int idx, const QVariant &value)
QQmlRefPointer< QQmlOpenMetaObjectType > type
QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, QObject *obj)
QVariant & propertyValueRef(int idx)
QDynamicMetaObjectData * parent
void init(const QMetaObject *metaObj)
QHash< QByteArray, int > names
QSet< QQmlOpenMetaObject * > referers
Combined button and popup list for selecting options.