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