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 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | QQmlVMEResolvedList::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 | |
67 | QQmlVMEResolvedList::~QQmlVMEResolvedList() = default; |
68 | |
69 | void QQmlVMEResolvedList::activateSignal() const |
70 | { |
71 | m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()), nullptr); |
72 | } |
73 | |
74 | void 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 | |
81 | void QQmlVMEMetaObject::list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o) |
82 | { |
83 | QQmlVMEResolvedList(prop).list()->append(t: o); |
84 | } |
85 | |
86 | static qsizetype list_count(QQmlListProperty<QObject> *prop) |
87 | { |
88 | return QQmlVMEResolvedList(prop).list()->size(); |
89 | } |
90 | |
91 | static QObject *list_at(QQmlListProperty<QObject> *prop, qsizetype index) |
92 | { |
93 | return QQmlVMEResolvedList(prop).list()->at(i: index); |
94 | } |
95 | |
96 | void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop) |
97 | { |
98 | const QQmlVMEResolvedList resolved(prop); |
99 | resolved.list()->clear(); |
100 | resolved.activateSignal(); |
101 | } |
102 | |
103 | void QQmlVMEMetaObject::list_clear_nosignal(QQmlListProperty<QObject> *prop) |
104 | { |
105 | QQmlVMEResolvedList(prop).list()->clear(); |
106 | } |
107 | |
108 | static 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 | |
115 | static void list_removeLast(QQmlListProperty<QObject> *prop) |
116 | { |
117 | const QQmlVMEResolvedList resolved(prop); |
118 | resolved.list()->removeLast(); |
119 | resolved.activateSignal(); |
120 | } |
121 | |
122 | QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr() |
123 | : QQmlGuard<QObject>(QQmlVMEVariantQObjectPtr::objectDestroyedImpl, nullptr), m_target(nullptr), m_index(-1) |
124 | { |
125 | } |
126 | |
127 | void 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 | |
148 | void QQmlVMEVariantQObjectPtr::setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index) |
149 | { |
150 | m_target = target; |
151 | m_index = index; |
152 | setObject(obj); |
153 | } |
154 | |
155 | class QQmlVMEMetaObjectEndpoint : public QQmlNotifierEndpoint |
156 | { |
157 | public: |
158 | QQmlVMEMetaObjectEndpoint(); |
159 | void tryConnect(); |
160 | |
161 | enum Tag { |
162 | NoTag, |
163 | EndPointIsConnected |
164 | }; |
165 | |
166 | QTaggedPointer<QQmlVMEMetaObject, Tag> metaObject; |
167 | }; |
168 | |
169 | QQmlVMEMetaObjectEndpoint::QQmlVMEMetaObjectEndpoint() |
170 | : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlVMEMetaObjectEndpoint) |
171 | { |
172 | } |
173 | |
174 | void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **) |
175 | { |
176 | QQmlVMEMetaObjectEndpoint *vmee = static_cast<QQmlVMEMetaObjectEndpoint*>(e); |
177 | vmee->tryConnect(); |
178 | } |
179 | |
180 | void 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 | |
222 | QQmlInterceptorMetaObject::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 | |
240 | QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject() |
241 | { |
242 | |
243 | } |
244 | |
245 | void 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 | |
261 | int 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 | |
271 | bool 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 | |
363 | static 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 | |
372 | QMetaObject *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 | |
385 | QQmlVMEMetaObject::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 | |
413 | QQmlVMEMetaObject::~QQmlVMEMetaObject() |
414 | { |
415 | if (parent.isT1()) parent.asT1()->objectDestroyed(object); |
416 | delete [] aliasEndpoints; |
417 | |
418 | qDeleteAll(c: varObjectGuards); |
419 | } |
420 | |
421 | QV4::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 | |
435 | void 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 | |
442 | void 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 | |
449 | void 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 | |
456 | void 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 | |
465 | void 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 | |
483 | int 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 | |
496 | bool 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 | |
509 | double 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 | |
522 | QString 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 | |
535 | QUrl 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 | |
549 | QDate 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 | |
563 | QTime 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 | |
577 | QDateTime 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) |
592 | QRegularExpression 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 | |
607 | QSizeF 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 | |
621 | QPointF 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 | |
635 | QObject* 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 | |
649 | QVector<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 | |
665 | QRectF 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 | |
679 | int 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 | |
1138 | QV4::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 | |
1152 | QV4::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 | |
1162 | QVariant 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 | |
1177 | void 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 | |
1238 | void 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 | |
1290 | QV4::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 |
1304 | void 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 | |
1322 | QV4::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 | |
1331 | void 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 | |
1341 | void QQmlVMEMetaObject::ensureQObjectWrapper() |
1342 | { |
1343 | Q_ASSERT(cache); |
1344 | QV4::QObjectWrapper::wrap(engine, object); |
1345 | } |
1346 | |
1347 | void 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 | |
1358 | bool 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 | |
1385 | void 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 | |
1405 | void 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 | */ |
1418 | void 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 | |
1423 | QQmlVMEMetaObject *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 | |
1433 | QQmlVMEMetaObject *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 | */ |
1447 | QQmlVMEMetaObject *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 | |
1457 | QQmlVMEVariantQObjectPtr *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 | |
1469 | QT_END_NAMESPACE |
1470 | |