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
qqmlobjectcreator.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
6#include <private/qjsvalue_p.h>
7#include <private/qqmlanybinding_p.h>
8#include <private/qqmlbinding_p.h>
9#include <private/qqmlboundsignal_p.h>
10#include <private/qqmlcomponent_p.h>
11#include <private/qqmlcomponentattached_p.h>
12#include <private/qqmlcustomparser_p.h>
13#include <private/qqmldebugconnector_p.h>
14#include <private/qqmldebugserviceinterfaces_p.h>
15#include <private/qqmlengine_p.h>
16#include <private/qqmlpropertybinding_p.h>
17#include <private/qqmlpropertyvalueinterceptor_p.h>
18#include <private/qqmlscriptdata_p.h>
19#include <private/qqmlscriptstring_p.h>
20#include <private/qqmlsourcecoordinate_p.h>
21#include <private/qqmlstringconverters_p.h>
22#include <private/qqmlvaluetypeproxybinding_p.h>
23#include <private/qqmlvme_p.h>
24#include <private/qqmlvmemetaobject_p.h>
25#include <private/qv4function_p.h>
26#include <private/qv4functionobject_p.h>
27#include <private/qv4generatorobject_p.h>
28#include <private/qv4qobjectwrapper_p.h>
29#include <private/qv4referenceobject_p.h>
30#include <private/qv4resolvedtypereference_p.h>
31
32#include <qtqml_tracepoints_p.h>
33
34#include <QtCore/qscopedvaluerollback.h>
35#include <QtCore/qloggingcategory.h>
36
37Q_STATIC_LOGGING_CATEGORY(lcQmlDefaultMethod, "qt.qml.defaultmethod")
38
39QT_USE_NAMESPACE
40
41Q_TRACE_PREFIX(qtqml,
42"namespace QV4 {" \
43"struct ExecutionEngine;"\
44"class ExecutableCompilationUnit;"\
45"namespace CompiledData {"\
46"struct Object;"\
47"}}"\
48"class QQmlEngine;"
49)
50
51Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_entry, const QV4::ExecutableCompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url)
52Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_exit, const QString &typeName)
53
54QQmlObjectCreator::QQmlObjectCreator(
55 const QQmlRefPointer<QQmlContextData> &parentContext,
56 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
57 const QQmlRefPointer<QQmlContextData> &creationContext,
58 const QString &inlineComponentName,
59 QQmlIncubatorPrivate *incubator)
60 : phase(Startup)
61 , m_inlineComponentName(inlineComponentName)
62 , compilationUnit(compilationUnit)
63 , propertyCaches(compilationUnit->propertyCachesPtr())
64 , sharedState(new QQmlObjectCreatorSharedState, QQmlRefPointer<QQmlObjectCreatorSharedState>::Adopt)
65 , topLevelCreator(true)
66 , isContextObject(true)
67 , incubator(incubator)
68{
69 init(parentContext);
70
71 sharedState->componentAttached = nullptr;
72 sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
73 sharedState->creationContext = creationContext;
74 sharedState->rootContext.reset();
75 sharedState->hadTopLevelRequiredProperties = false;
76
77 if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) {
78 Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler,
79 sharedState->profiler.init(profiler));
80 } else {
81 Q_UNUSED(profiler);
82 }
83}
84
85QQmlObjectCreator::QQmlObjectCreator(const QQmlRefPointer<QQmlContextData> &parentContext,
86 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QString inlineComponentName,
87 QQmlObjectCreatorSharedState *inheritedSharedState, bool isContextObject)
88 : phase(Startup)
89 , m_inlineComponentName(inlineComponentName)
90 , compilationUnit(compilationUnit)
91 , propertyCaches(compilationUnit->propertyCachesPtr())
92 , sharedState(inheritedSharedState)
93 , topLevelCreator(false)
94 , isContextObject(isContextObject)
95 , incubator(nullptr)
96{
97 init(parentContext);
98}
99
100void QQmlObjectCreator::init(const QQmlRefPointer<QQmlContextData> &providedParentContext)
101{
102 parentContext = providedParentContext;
103 engine = parentContext->engine();
104 v4 = engine->handle();
105
106 Q_ASSERT(compilationUnit);
107 Q_ASSERT(compilationUnit->engine == v4);
108 if (!compilationUnit->runtimeStrings)
109 compilationUnit->populate();
110
111 qmlUnit = compilationUnit->unitData();
112 _qobject = nullptr;
113 _scopeObject = nullptr;
114 _bindingTarget = nullptr;
115 _valueTypeProperty = nullptr;
116 _compiledObject = nullptr;
117 _compiledObjectIndex = -1;
118 _ddata = nullptr;
119 _vmeMetaObject = nullptr;
120 _qmlContext = nullptr;
121}
122
123QQmlObjectCreator::~QQmlObjectCreator()
124{
125 if (topLevelCreator) {
126 {
127 // This signals to other methods further up the stack that we have
128 // "recursed" and they should be aborted.
129 QQmlObjectCreatorRecursionWatcher watcher(this);
130 }
131 while (sharedState->componentAttached) {
132 QQmlComponentAttached *a = sharedState->componentAttached;
133 a->removeFromList();
134 }
135 }
136}
137
138QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt, int flags)
139{
140 if (phase == CreatingObjectsPhase2) {
141 phase = ObjectsCreated;
142 return context->contextObject();
143 }
144 Q_ASSERT(phase == Startup);
145 phase = CreatingObjects;
146
147 int objectToCreate;
148 bool isComponentRoot = false; // either a "real" component of or an inline component
149
150 if (subComponentIndex == -1) {
151 objectToCreate = /*root object*/0;
152 isComponentRoot = true;
153 } else {
154 Q_ASSERT(subComponentIndex >= 0);
155 if (flags & CreationFlags::InlineComponent) {
156 if (compilationUnit->componentsAreBound()
157 && compilationUnit != parentContext->typeCompilationUnit()) {
158 recordError({}, tr("Cannot instantiate bound inline component in different file"));
159 phase = ObjectsCreated;
160 return nullptr;
161 }
162 objectToCreate = subComponentIndex;
163 isComponentRoot = true;
164 } else {
165 Q_ASSERT(flags & CreationFlags::NormalObject);
166 if (compilationUnit->componentsAreBound()
167 && sharedState->creationContext != parentContext) {
168 recordError({}, tr("Cannot instantiate bound component "
169 "outside its creation context"));
170 phase = ObjectsCreated;
171 return nullptr;
172 }
173 const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
174 objectToCreate = compObj->bindingTable()->value.objectIndex;
175 }
176 }
177
178 context = QQmlEnginePrivate::get(engine)->createInternalContext(
179 compilationUnit, parentContext, subComponentIndex, isComponentRoot);
180
181 if (!sharedState->rootContext) {
182 sharedState->rootContext = context;
183 sharedState->rootContext->setIncubator(incubator);
184 sharedState->rootContext->setRootObjectInCreation(true);
185 }
186
187 QV4::Scope scope(v4);
188
189 Q_ASSERT(sharedState->allJavaScriptObjects.canTrack() || topLevelCreator);
190 if (topLevelCreator)
191 sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(scope);
192
193 if (!isComponentRoot && sharedState->creationContext) {
194 // otherwise QQmlEnginePrivate::createInternalContext() handles it
195 QV4::ScopedValue scripts(scope, sharedState->creationContext->importedScripts());
196 context->setImportedScripts(v4, scripts);
197 }
198
199 QObject *instance = createInstance(objectToCreate, parent, /*isContextObject*/true);
200 if (instance) {
201 QQmlData *ddata = QQmlData::get(instance);
202 Q_ASSERT(ddata);
203 ddata->compilationUnit = compilationUnit;
204 }
205
206 if (topLevelCreator)
207 sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
208
209 phase = CreatingObjectsPhase2;
210
211 if (interrupt && interrupt->shouldInterrupt())
212 return nullptr;
213
214 phase = ObjectsCreated;
215
216 if (instance) {
217 if (QQmlEngineDebugService *service
218 = QQmlDebugConnector::service<QQmlEngineDebugService>()) {
219 if (!parentContext->isInternal())
220 parentContext->asQQmlContextPrivate()->appendInstance(instance);
221 service->objectCreated(engine, instance);
222 } else if (!parentContext->isInternal() && QQmlDebugConnector::service<QV4DebugService>()) {
223 parentContext->asQQmlContextPrivate()->appendInstance(instance);
224 }
225 }
226
227 return instance;
228}
229
230void QQmlObjectCreator::beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &newContext)
231{
232 context = newContext;
233 sharedState->rootContext = newContext;
234
235 Q_ASSERT(topLevelCreator);
236 Q_ASSERT(!sharedState->allJavaScriptObjects.canTrack());
237
238 // FIXME (QTBUG-122956): allocating from the short lived scope does not make any sense
239 QV4::Scope valueScope(v4);
240 sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(valueScope);
241}
242
243void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
244 const QQmlPropertyPrivate *qmlProperty,
245 const QV4::CompiledData::Binding *binding)
246{
247 doPopulateDeferred(instance, deferredIndex, [this, qmlProperty, binding]() {
248 Q_ASSERT(qmlProperty);
249 Q_ASSERT(binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding));
250
251 QQmlListProperty<QObject> savedList;
252 qSwap(_currentList, savedList);
253
254 const QQmlPropertyData &property = qmlProperty->core;
255
256 if (property.propType().flags().testFlag(QMetaType::IsQmlList)) {
257 void *argv[1] = { (void*)&_currentList };
258 QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property.coreIndex(), argv);
259 } else if (_currentList.object) {
260 _currentList = QQmlListProperty<QObject>();
261 }
262
263 setPropertyBinding(&property, binding);
264
265 qSwap(_currentList, savedList);
266 });
267}
268
269void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex)
270{
271 doPopulateDeferred(instance, deferredIndex, [this]() { setupBindings(ApplyDeferred); });
272}
273
274bool QQmlObjectCreator::populateDeferredProperties(QObject *instance,
275 const QQmlData::DeferredData *deferredData)
276{
277 beginPopulateDeferred(deferredData->context);
278 populateDeferred(instance, deferredData->deferredIdx);
279 finalizePopulateDeferred();
280 return errors.isEmpty();
281}
282
283void QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex,
284 const QV4::CompiledData::Binding *binding)
285{
286 if (binding) {
287 populateDeferred(qmlProperty.object(), deferredIndex, QQmlPropertyPrivate::get(qmlProperty),
288 binding);
289 } else {
290 populateDeferred(qmlProperty.object(), deferredIndex);
291 }
292}
293
294void QQmlObjectCreator::populateDeferredInstance(
295 QObject *outerObject, int deferredIndex, int index, QObject *instance,
296 QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty,
297 const QV4::CompiledData::Binding *binding)
298{
299 doPopulateDeferred(outerObject, deferredIndex, [&]() {
300 populateInstance(index, instance, bindingTarget, valueTypeProperty, binding);
301 });
302}
303
304void QQmlObjectCreator::finalizePopulateDeferred()
305{
306 phase = ObjectsCreated;
307}
308
309void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding)
310{
311 QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite;
312 QV4::Scope scope(v4);
313
314 QMetaType propertyType = property->propType();
315
316 if (property->isEnum()) {
317 if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum) ||
318 // TODO: For historical reasons you can assign any number to an enum property alias
319 // This can be fixed with an opt-out mechanism, for example a pragma.
320 (property->isAlias() && binding->isNumberBinding())) {
321 propertyType = property->propType().underlyingType();
322 } else {
323 // ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
324 QVariant value = compilationUnit->bindingValueAsString(binding);
325 bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context);
326 Q_ASSERT(ok);
327 Q_UNUSED(ok);
328 return;
329 }
330 }
331
332 auto assertOrNull = [&](bool ok)
333 {
334 Q_ASSERT(ok || binding->type() == QV4::CompiledData::Binding::Type_Null);
335 Q_UNUSED(ok);
336 };
337
338 auto assertType = [&](QV4::CompiledData::Binding::Type type)
339 {
340 Q_ASSERT(binding->type()== type || binding->type() == QV4::CompiledData::Binding::Type_Null);
341 Q_UNUSED(type);
342 };
343
344 if (property->isQObject()) {
345 if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
346 QObject *value = nullptr;
347 const bool ok = property->writeProperty(_qobject, &value, propertyWriteFlags);
348 Q_ASSERT(ok);
349 Q_UNUSED(ok);
350 return;
351 }
352 }
353
354 switch (propertyType.id()) {
355 case QMetaType::QVariant: {
356 if (binding->type() == QV4::CompiledData::Binding::Type_Number) {
357 double n = compilationUnit->bindingValueAsNumber(binding);
358 if (double(int(n)) == n) {
359 if (property->isVarProperty()) {
360 _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromInt32(int(n)));
361 } else {
362 int i = int(n);
363 QVariant value(i);
364 property->writeProperty(_qobject, &value, propertyWriteFlags);
365 }
366 } else {
367 if (property->isVarProperty()) {
368 _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromDouble(n));
369 } else {
370 QVariant value(n);
371 property->writeProperty(_qobject, &value, propertyWriteFlags);
372 }
373 }
374 } else if (binding->type() == QV4::CompiledData::Binding::Type_Boolean) {
375 if (property->isVarProperty()) {
376 _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromBoolean(binding->valueAsBoolean()));
377 } else {
378 QVariant value(binding->valueAsBoolean());
379 property->writeProperty(_qobject, &value, propertyWriteFlags);
380 }
381 } else if (binding->type() == QV4::CompiledData::Binding::Type_Null) {
382 if (property->isVarProperty()) {
383 _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::nullValue());
384 } else {
385 QVariant nullValue = QVariant::fromValue(nullptr);
386 property->writeProperty(_qobject, &nullValue, propertyWriteFlags);
387 }
388 } else {
389 QString stringValue = compilationUnit->bindingValueAsString(binding);
390 if (property->isVarProperty()) {
391 QV4::ScopedString s(scope, v4->newString(stringValue));
392 _vmeMetaObject->setVMEProperty(property->coreIndex(), s);
393 } else {
394 QVariant value = stringValue;
395 property->writeProperty(_qobject, &value, propertyWriteFlags);
396 }
397 }
398 }
399 break;
400 case QMetaType::QString: {
401 assertOrNull(binding->evaluatesToString());
402 QString value = compilationUnit->bindingValueAsString(binding);
403 property->writeProperty(_qobject, &value, propertyWriteFlags);
404 }
405 break;
406 case QMetaType::QStringList: {
407 assertOrNull(binding->evaluatesToString());
408 QStringList value(compilationUnit->bindingValueAsString(binding));
409 property->writeProperty(_qobject, &value, propertyWriteFlags);
410 }
411 break;
412 case QMetaType::QByteArray: {
413 assertType(QV4::CompiledData::Binding::Type_String);
414 QByteArray value(compilationUnit->bindingValueAsString(binding).toUtf8());
415 property->writeProperty(_qobject, &value, propertyWriteFlags);
416 }
417 break;
418 case QMetaType::QUrl: {
419 assertType(QV4::CompiledData::Binding::Type_String);
420 const QString string = compilationUnit->bindingValueAsString(binding);
421 QUrl value = (!string.isEmpty() && QQmlPropertyPrivate::resolveUrlsOnAssignment())
422 ? compilationUnit->finalUrl().resolved(QUrl(string))
423 : QUrl(string);
424 property->writeProperty(_qobject, &value, propertyWriteFlags);
425 }
426 break;
427 case QMetaType::UInt: {
428 assertType(QV4::CompiledData::Binding::Type_Number);
429 double d = compilationUnit->bindingValueAsNumber(binding);
430 uint value = uint(d);
431 property->writeProperty(_qobject, &value, propertyWriteFlags);
432 break;
433 }
434 break;
435 case QMetaType::Int: {
436 assertType(QV4::CompiledData::Binding::Type_Number);
437 double d = compilationUnit->bindingValueAsNumber(binding);
438 int value = int(d);
439 property->writeProperty(_qobject, &value, propertyWriteFlags);
440 break;
441 }
442 break;
443 case QMetaType::SChar: {
444 assertType(QV4::CompiledData::Binding::Type_Number);
445 double d = compilationUnit->bindingValueAsNumber(binding);
446 qint8 value = qint8(d);
447 property->writeProperty(_qobject, &value, propertyWriteFlags);
448 break;
449 }
450 case QMetaType::UChar: {
451 assertType(QV4::CompiledData::Binding::Type_Number);
452 double d = compilationUnit->bindingValueAsNumber(binding);
453 quint8 value = quint8(d);
454 property->writeProperty(_qobject, &value, propertyWriteFlags);
455 break;
456 }
457 case QMetaType::Short: {
458 assertType(QV4::CompiledData::Binding::Type_Number);
459 double d = compilationUnit->bindingValueAsNumber(binding);
460 qint16 value = qint16(d);
461 property->writeProperty(_qobject, &value, propertyWriteFlags);
462 break;
463 }
464 case QMetaType::UShort: {
465 assertType(QV4::CompiledData::Binding::Type_Number);
466 double d = compilationUnit->bindingValueAsNumber(binding);
467 quint16 value = quint16(d);
468 property->writeProperty(_qobject, &value, propertyWriteFlags);
469 break;
470 }
471 case QMetaType::LongLong: {
472 assertType(QV4::CompiledData::Binding::Type_Number);
473 double d = compilationUnit->bindingValueAsNumber(binding);
474 qint64 value = qint64(d);
475 property->writeProperty(_qobject, &value, propertyWriteFlags);
476 break;
477 }
478 case QMetaType::ULongLong: {
479 assertType(QV4::CompiledData::Binding::Type_Number);
480 double d = compilationUnit->bindingValueAsNumber(binding);
481 quint64 value = quint64(d);
482 property->writeProperty(_qobject, &value, propertyWriteFlags);
483 break;
484 }
485 break;
486 case QMetaType::Float: {
487 assertType(QV4::CompiledData::Binding::Type_Number);
488 float value = float(compilationUnit->bindingValueAsNumber(binding));
489 property->writeProperty(_qobject, &value, propertyWriteFlags);
490 }
491 break;
492 case QMetaType::Double: {
493 assertType(QV4::CompiledData::Binding::Type_Number);
494 double value = compilationUnit->bindingValueAsNumber(binding);
495 property->writeProperty(_qobject, &value, propertyWriteFlags);
496 }
497 break;
498 case QMetaType::QColor: {
499 bool ok = false;
500 QVariant data = QQmlStringConverters::colorFromString(
501 compilationUnit->bindingValueAsString(binding), &ok);
502 Q_ASSERT(ok); // We've checked this in QQmlPropertyValidator
503 property->writeProperty(_qobject, data.data(), propertyWriteFlags);
504 }
505 break;
506#if QT_CONFIG(datestring)
507 case QMetaType::QDate: {
508 bool ok = false;
509 QDate value = QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
510 assertOrNull(ok);
511 property->writeProperty(_qobject, &value, propertyWriteFlags);
512 }
513 break;
514 case QMetaType::QTime: {
515 bool ok = false;
516 QTime value = QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
517 assertOrNull(ok);
518 property->writeProperty(_qobject, &value, propertyWriteFlags);
519 }
520 break;
521 case QMetaType::QDateTime: {
522 bool ok = false;
523 QDateTime value = QQmlStringConverters::dateTimeFromString(
524 compilationUnit->bindingValueAsString(binding), &ok);
525 assertOrNull(ok);
526 property->writeProperty(_qobject, &value, propertyWriteFlags);
527 }
528 break;
529#endif // datestring
530 case QMetaType::QPoint: {
531 bool ok = false;
532 QPoint value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok).toPoint();
533 assertOrNull(ok);
534 property->writeProperty(_qobject, &value, propertyWriteFlags);
535 }
536 break;
537 case QMetaType::QPointF: {
538 bool ok = false;
539 QPointF value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
540 assertOrNull(ok);
541 property->writeProperty(_qobject, &value, propertyWriteFlags);
542 }
543 break;
544 case QMetaType::QSize: {
545 bool ok = false;
546 QSize value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok).toSize();
547 assertOrNull(ok);
548 property->writeProperty(_qobject, &value, propertyWriteFlags);
549 }
550 break;
551 case QMetaType::QSizeF: {
552 bool ok = false;
553 QSizeF value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
554 assertOrNull(ok);
555 property->writeProperty(_qobject, &value, propertyWriteFlags);
556 }
557 break;
558 case QMetaType::QRect: {
559 bool ok = false;
560 QRect value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok).toRect();
561 assertOrNull(ok);
562 property->writeProperty(_qobject, &value, propertyWriteFlags);
563 }
564 break;
565 case QMetaType::QRectF: {
566 bool ok = false;
567 QRectF value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
568 assertOrNull(ok);
569 property->writeProperty(_qobject, &value, propertyWriteFlags);
570 }
571 break;
572 case QMetaType::Bool: {
573 assertType(QV4::CompiledData::Binding::Type_Boolean);
574 bool value = binding->valueAsBoolean();
575 property->writeProperty(_qobject, &value, propertyWriteFlags);
576 }
577 break;
578 case QMetaType::QVector2D:
579 case QMetaType::QVector3D:
580 case QMetaType::QVector4D:
581 case QMetaType::QQuaternion: {
582 QVariant result = QQmlValueTypeProvider::createValueType(
583 compilationUnit->bindingValueAsString(binding), propertyType);
584 assertOrNull(result.isValid());
585 property->writeProperty(_qobject, result.data(), propertyWriteFlags);
586 break;
587 }
588 default: {
589 // generate single literal value assignment to a list property if required
590 if (propertyType == QMetaType::fromType<QList<qreal>>()) {
591 assertType(QV4::CompiledData::Binding::Type_Number);
592 QList<qreal> value;
593 value.append(compilationUnit->bindingValueAsNumber(binding));
594 property->writeProperty(_qobject, &value, propertyWriteFlags);
595 break;
596 } else if (propertyType == QMetaType::fromType<QList<int>>()) {
597 assertType(QV4::CompiledData::Binding::Type_Number);
598 double n = compilationUnit->bindingValueAsNumber(binding);
599 QList<int> value;
600 value.append(int(n));
601 property->writeProperty(_qobject, &value, propertyWriteFlags);
602 break;
603 } else if (propertyType == QMetaType::fromType<QList<bool>>()) {
604 assertType(QV4::CompiledData::Binding::Type_Boolean);
605 QList<bool> value;
606 value.append(binding->valueAsBoolean());
607 property->writeProperty(_qobject, &value, propertyWriteFlags);
608 break;
609 } else if (propertyType == QMetaType::fromType<QList<QUrl>>()) {
610 assertType(QV4::CompiledData::Binding::Type_String);
611 const QUrl url(compilationUnit->bindingValueAsString(binding));
612 QList<QUrl> value {
613 QQmlPropertyPrivate::resolveUrlsOnAssignment()
614 ? compilationUnit->finalUrl().resolved(url)
615 : url
616 };
617 property->writeProperty(_qobject, &value, propertyWriteFlags);
618 break;
619 } else if (propertyType == QMetaType::fromType<QList<QString>>()) {
620 assertOrNull(binding->evaluatesToString());
621 QList<QString> value;
622 value.append(compilationUnit->bindingValueAsString(binding));
623 property->writeProperty(_qobject, &value, propertyWriteFlags);
624 break;
625 } else if (propertyType == QMetaType::fromType<QJSValue>()) {
626 QJSValue value;
627 switch (binding->type()) {
628 case QV4::CompiledData::Binding::Type_Boolean:
629 value = QJSValue(binding->valueAsBoolean());
630 break;
631 case QV4::CompiledData::Binding::Type_Number: {
632 const double n = compilationUnit->bindingValueAsNumber(binding);
633 if (double(int(n)) == n)
634 value = QJSValue(int(n));
635 else
636 value = QJSValue(n);
637 break;
638 }
639 case QV4::CompiledData::Binding::Type_Null:
640 value = QJSValue::NullValue;
641 break;
642 default:
643 value = QJSValue(compilationUnit->bindingValueAsString(binding));
644 break;
645 }
646 property->writeProperty(_qobject, &value, propertyWriteFlags);
647 break;
648 } else {
649 QVariant source;
650 switch (binding->type()) {
651 case QV4::CompiledData::Binding::Type_Boolean:
652 source = binding->valueAsBoolean();
653 break;
654 case QV4::CompiledData::Binding::Type_Number: {
655 const double n = compilationUnit->bindingValueAsNumber(binding);
656 if (double(int(n)) == n)
657 source = int(n);
658 else
659 source = n;
660 break;
661 }
662 case QV4::CompiledData::Binding::Type_Null:
663 source = QVariant::fromValue<std::nullptr_t>(nullptr);
664 break;
665 case QV4::CompiledData::Binding::Type_Invalid:
666 break;
667 default:
668 source = compilationUnit->bindingValueAsString(binding);
669 break;
670 }
671
672 QVariant target = QQmlValueTypeProvider::createValueType(
673 source, propertyType, engine->handle());
674 if (target.isValid()) {
675 property->writeProperty(_qobject, target.data(), propertyWriteFlags);
676 break;
677 }
678 }
679
680 // string converters are not exposed, so ending up here indicates an error
681 QString stringValue = compilationUnit->bindingValueAsString(binding);
682 QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex());
683 recordError(binding->location, tr("Cannot assign value %1 to property"
684" %2").arg(stringValue, QString::fromUtf8(metaProperty.name())));
685 }
686 break;
687 }
688}
689
690static QQmlType qmlTypeForObject(QObject *object)
691{
692 QQmlType type;
693 const QMetaObject *mo = object->metaObject();
694 while (mo && !type.isValid()) {
695 type = QQmlMetaType::qmlType(mo);
696 mo = mo->superClass();
697 }
698 return type;
699}
700
701void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
702{
703 QQmlListProperty<QObject> savedList;
704 qSwap(_currentList, savedList);
705
706 const QV4::CompiledData::BindingPropertyData *propertyData
707 = compilationUnit->bindingPropertyDataPerObjectAt(_compiledObjectIndex);
708
709 if (_compiledObject->idNameIndex) {
710 const QQmlPropertyData *idProperty = propertyData->last();
711 Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id"));
712 if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType().id() == QMetaType::QString) {
713 QV4::CompiledData::Binding idBinding;
714 idBinding.propertyNameIndex = 0; // Not used
715 idBinding.clearFlags();
716 idBinding.setType(QV4::CompiledData::Binding::Type_String);
717 idBinding.stringIndex = _compiledObject->idNameIndex;
718 idBinding.location = _compiledObject->location; // ###
719 idBinding.value.nullMarker = 0; // zero the value field to make codechecker happy
720 setPropertyValue(idProperty, &idBinding);
721 }
722 }
723
724 // ### this is best done through type-compile-time binding skip lists.
725 if (_valueTypeProperty) {
726 QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex()));
727
728 if (binding && binding->kind() != QQmlAbstractBinding::ValueTypeProxy) {
729 QQmlPropertyPrivate::removeBinding(
730 _bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex()),
731 QQmlPropertyPrivate::OverrideSticky);
732 } else if (binding) {
733 QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding *>(binding);
734
735 if (qmlTypeForObject(_bindingTarget).isValid()) {
736 quint32 bindingSkipList = 0;
737
738 const QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultPropertyOrAlias != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty();
739
740 const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
741 for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
742 const QQmlPropertyData *property = binding->propertyNameIndex != 0
743 ? _propertyCache->property(stringAt(binding->propertyNameIndex),
744 _qobject, context)
745 : defaultProperty;
746 if (property)
747 bindingSkipList |= (1 << property->coreIndex());
748 }
749
750 proxy->removeBindings(bindingSkipList);
751 }
752 }
753 }
754
755 int currentListPropertyIndex = -1;
756
757 const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
758 for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
759 const QQmlPropertyData *const property = propertyData->at(i);
760 if (property) {
761 const QQmlPropertyData *targetProperty = property;
762 if (targetProperty->isAlias()) {
763 // follow alias
764 QQmlPropertyIndex originalIndex(targetProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1);
765 auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(_bindingTarget, originalIndex);
766 QQmlData *data = QQmlData::get(targetObject);
767 Q_ASSERT(data && data->propertyCache);
768 targetProperty = data->propertyCache->property(targetIndex.coreIndex());
769 sharedState->requiredProperties.remove({targetObject, targetProperty});
770 }
771 sharedState->requiredProperties.remove({_bindingTarget, property});
772 }
773
774
775 if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
776 continue;
777
778 if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding)) {
779 if (!(mode & ApplyDeferred))
780 continue;
781 } else if (!(mode & ApplyImmediate)) {
782 continue;
783 }
784
785 if (property && property->propType().flags().testFlag(QMetaType::IsQmlList)) {
786 if (property->coreIndex() != currentListPropertyIndex) {
787 void *argv[1] = { (void*)&_currentList };
788 QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv);
789 currentListPropertyIndex = property->coreIndex();
790
791 // manage override behavior
792 const QMetaObject *const metaobject = _qobject->metaObject();
793 const int qmlListBehavorClassInfoIndex = metaobject->indexOfClassInfo("QML.ListPropertyAssignBehavior");
794 if (qmlListBehavorClassInfoIndex != -1) { // QML.ListPropertyAssignBehavior class info is set
795 const char *overrideBehavior =
796 metaobject->classInfo(qmlListBehavorClassInfoIndex).value();
797 if (!strcmp(overrideBehavior,
798 "Replace")) {
799 if (_currentList.clear) {
800 _currentList.clear(&_currentList);
801 }
802 } else {
803 bool isDefaultProperty =
804 (property->name(_qobject)
805 == QString::fromUtf8(
806 metaobject
807 ->classInfo(metaobject->indexOfClassInfo(
808 "DefaultProperty"))
809 .value()));
810 if (!isDefaultProperty
811 && (!strcmp(overrideBehavior,
812 "ReplaceIfNotDefault"))) {
813 if (_currentList.clear) {
814 _currentList.clear(&_currentList);
815 }
816 }
817 }
818 }
819 }
820 } else if (_currentList.object) {
821 _currentList = QQmlListProperty<QObject>();
822 currentListPropertyIndex = -1;
823 }
824
825 if (!setPropertyBinding(property, binding))
826 return;
827 }
828
829 qSwap(_currentList, savedList);
830}
831
832bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding)
833{
834 const QV4::CompiledData::Binding::Type bindingType = binding->type();
835 if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
836 const QV4::CompiledData::Object *obj = compilationUnit->objectAt(binding->value.objectIndex);
837 QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj);
838
839 Q_ASSERT(stringAt(obj->inheritedTypeNameIndex).isEmpty());
840 QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
841 Q_ASSERT(tr);
842 QQmlType attachedType = tr->type();
843 QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(engine);
844 if (!attachedType.isValid()) {
845 QQmlTypeNameCache::Result res
846 = context->imports()->query(stringAt(binding->propertyNameIndex), typeLoader);
847 if (res.isValid())
848 attachedType = res.type;
849 else
850 return false;
851 }
852
853 Q_QML_OC_PROFILE(
854 sharedState->profiler,
855 profiler.update(compilationUnit.data(), obj, attachedType.qmlTypeName(),
856 context->url()));
857
858 QObject *qmlObject = qmlAttachedPropertiesObject(
859 _qobject, attachedType.attachedPropertiesFunction(typeLoader));
860 if (!qmlObject) {
861 recordError(binding->location,
862 QStringLiteral("Could not create attached properties object '%1'")
863 .arg(QString::fromUtf8(attachedType.typeName())));
864 return false;
865 }
866
867 const size_t objectIndex = sharedState->allCreatedObjects.size();
868 sharedState->allCreatedObjects.push_back(qmlObject);
869 const QQmlType attachedObjectType
870 = QQmlMetaType::qmlType(attachedType.attachedPropertiesType(typeLoader));
871 const int parserStatusCast = attachedObjectType.parserStatusCast();
872 QQmlParserStatus *parserStatus = nullptr;
873 if (parserStatusCast != -1)
874 parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(qmlObject) + parserStatusCast);
875 if (parserStatus) {
876 parserStatus->classBegin();
877 // we ignore them for profiling, because it doesn't interact well with the logic anyway
878 sharedState->allParserStatusCallbacks.push_back({objectIndex, parserStatusCast});
879 }
880
881 if (!populateInstance(binding->value.objectIndex, qmlObject, qmlObject,
882 /*value type property*/ nullptr, binding))
883 return false;
884 return true;
885 }
886
887 // ### resolve this at compile time
888 if (bindingProperty && bindingProperty->propType() == QMetaType::fromType<QQmlScriptString>()) {
889 QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding),
890 context->asQQmlContext(), _scopeObject);
891 ss.d.data()->bindingId = bindingType == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
892 ss.d.data()->lineNumber = binding->location.line();
893 ss.d.data()->columnNumber = binding->location.column();
894 ss.d.data()->isStringLiteral = bindingType == QV4::CompiledData::Binding::Type_String;
895 ss.d.data()->isNumberLiteral = bindingType == QV4::CompiledData::Binding::Type_Number;
896 ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding);
897
898 QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
899 QQmlPropertyData::RemoveBindingOnAliasWrite;
900 int propertyWriteStatus = -1;
901 void *argv[] = { &ss, nullptr, &propertyWriteStatus, &propertyWriteFlags };
902 QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv);
903 return true;
904 }
905
906 QObject *createdSubObject = nullptr;
907 if (bindingType == QV4::CompiledData::Binding::Type_Object) {
908 // This is not a top level object. Its required properties don't count towards the
909 // top level required properties.
910 QScopedValueRollback topLevelRequired(sharedState->hadTopLevelRequiredProperties);
911 createdSubObject = createInstance(binding->value.objectIndex, _bindingTarget);
912 if (!createdSubObject)
913 return false;
914 }
915
916 if (bindingType == QV4::CompiledData::Binding::Type_GroupProperty) {
917 const QV4::CompiledData::Object *obj = compilationUnit->objectAt(binding->value.objectIndex);
918 if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) {
919
920 QObject *groupObject = nullptr;
921 QQmlGadgetPtrWrapper *valueType = nullptr;
922 const QQmlPropertyData *valueTypeProperty = nullptr;
923 QObject *bindingTarget = _bindingTarget;
924 int groupObjectIndex = binding->value.objectIndex;
925
926 if (!bindingProperty) {
927 for (int i = 0, end = compilationUnit->objectCount(); i != end; ++i) {
928 const QV4::CompiledData::Object *external = compilationUnit->objectAt(i);
929 if (external->idNameIndex == binding->propertyNameIndex) {
930 bindingTarget = groupObject = context->idValue(external->objectId());
931 break;
932 }
933 }
934 if (!groupObject)
935 return true;
936 } else if (QQmlMetaType::isValueType(bindingProperty->propType())) {
937 valueType = QQmlGadgetPtrWrapper::instance(engine, bindingProperty->propType());
938 if (!valueType) {
939 recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex)));
940 return false;
941 }
942
943 valueType->read(_qobject, bindingProperty->coreIndex());
944
945 groupObject = valueType;
946 valueTypeProperty = bindingProperty;
947 } else if (bindingProperty->propType().flags() & QMetaType::PointerToQObject) {
948 void *argv[1] = { &groupObject };
949 QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv);
950 if (!groupObject) {
951 QQmlPropertyIndex index(bindingProperty->coreIndex());
952 auto anyBinding = QQmlAnyBinding::ofProperty(_qobject, index);
953 if (anyBinding) {
954 // if there is a binding, try to force-evaluate it now
955 // this might instantiate a necessary part of a grouped property
956 anyBinding.refresh();
957 QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv);
958 }
959 if (!groupObject) {
960 recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex)));
961 return false;
962 }
963 }
964
965 bindingTarget = groupObject;
966 } else {
967 recordError(
968 binding->location,
969 tr("Using grouped property syntax on %1 which has no properties")
970 .arg(stringAt(binding->propertyNameIndex)));
971 return false;
972 }
973
974 if (!populateInstance(groupObjectIndex, groupObject, bindingTarget, valueTypeProperty,
975 binding)) {
976 return false;
977 }
978
979 if (valueType) {
980 valueType->write(
981 _qobject, bindingProperty->coreIndex(),
982 QQmlPropertyData::BypassInterceptor,
983 QV4::ReferenceObject::AllProperties);
984 }
985
986 return true;
987 }
988 }
989
990 if (!bindingProperty) // ### error
991 return true;
992
993 const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
994 const bool allowedToRemoveBinding
995 = !(bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
996 && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment)
997 && !(bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
998 && !_valueTypeProperty;
999
1000 if (allowedToRemoveBinding) {
1001 if (bindingProperty->acceptsQBinding()) {
1002 removePendingBinding(_bindingTarget, bindingProperty->coreIndex());
1003 } else {
1004 QQmlPropertyPrivate::removeBinding(
1005 _bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()),
1006 QQmlPropertyPrivate::OverrideSticky);
1007 }
1008 }
1009
1010 if (bindingType == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
1011 if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
1012 || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver) {
1013 QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
1014 int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex());
1015 QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(
1016 _bindingTarget, signalIndex, context,
1017 _scopeObject, runtimeFunction, currentQmlContext());
1018
1019 if (bindingProperty->notifiesViaBindable()) {
1020 auto target = _bindingTarget;
1021 if (bindingProperty->isAlias()) {
1022 // If the property is an alias, we cannot obtain the bindable interface directly with qt_metacall
1023 // so instead, we resolve the alias to obtain the actual target
1024 // This should be faster than doing a detour through the metaobject of the target, and relying on
1025 // QMetaObject::metacall doing the correct resolution
1026 QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1);
1027 auto [aliasTargetObject, aliasTargetIndex] = QQmlPropertyPrivate::findAliasTarget(target, originalIndex);
1028 target = aliasTargetObject;
1029 QQmlData *data = QQmlData::get(target);
1030 Q_ASSERT(data && data->propertyCache);
1031 bindingProperty = data->propertyCache->property(aliasTargetIndex.coreIndex());
1032 }
1033 auto &observer = QQmlData::get(_scopeObject)->propertyObservers.emplace_back(expr);
1034 QUntypedBindable bindable;
1035 void *argv[] = { &bindable };
1036 target->qt_metacall(QMetaObject::BindableProperty, bindingProperty->coreIndex(), argv);
1037 Q_ASSERT(bindable.isValid());
1038 bindable.observe(&observer);
1039 } else {
1040 QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine);
1041 bs->takeExpression(expr);
1042 }
1043 } else if (bindingProperty->acceptsQBinding()) {
1044 QUntypedPropertyBinding qmlBinding;
1045 if (binding->isTranslationBinding()) {
1046 qmlBinding = QQmlTranslationPropertyBinding::create(bindingProperty, compilationUnit, binding);
1047 } else {
1048 QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
1049 QQmlPropertyIndex index(bindingProperty->coreIndex(), -1);
1050 qmlBinding = QQmlPropertyBinding::create(bindingProperty, runtimeFunction, _scopeObject, context, currentQmlContext(), _bindingTarget, index);
1051 }
1052 sharedState.data()->allQPropertyBindings.push_back(DeferredQPropertyBinding {_bindingTarget, bindingProperty->coreIndex(), qmlBinding });
1053
1054 QQmlData *data = QQmlData::get(_bindingTarget, true);
1055 data->setBindingBit(_bindingTarget, bindingProperty->coreIndex());
1056 } else {
1057 // When writing bindings to grouped properties implemented as value types,
1058 // such as point.x: { someExpression; }, then the binding is installed on
1059 // the point property (_qobjectForBindings) and after evaluating the expression,
1060 // the result is written to a value type virtual property, that contains the sub-index
1061 // of the "x" property.
1062 QQmlBinding::Ptr qmlBinding;
1063 const QQmlPropertyData *targetProperty = bindingProperty;
1064 const QQmlPropertyData *subprop = nullptr;
1065 if (_valueTypeProperty) {
1066 targetProperty = _valueTypeProperty;
1067 subprop = bindingProperty;
1068 }
1069 if (binding->isTranslationBinding()) {
1070 qmlBinding = QQmlBinding::createTranslationBinding(
1071 compilationUnit, binding, _scopeObject, context);
1072 } else {
1073 QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
1074 qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject,
1075 context, currentQmlContext());
1076 }
1077
1078 auto bindingTarget = _bindingTarget;
1079 auto valueTypeProperty = _valueTypeProperty;
1080 auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) mutable -> bool {
1081 if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias())
1082 return false;
1083
1084 sharedState->allCreatedBindings.push_back(qmlBinding);
1085
1086 if (bindingProperty->isAlias()) {
1087 QQmlPropertyPrivate::setBinding(qmlBinding.data(), QQmlPropertyPrivate::DontEnable);
1088 } else {
1089 qmlBinding->addToObject();
1090
1091 if (!valueTypeProperty) {
1092 QQmlData *targetDeclarativeData = QQmlData::get(bindingTarget);
1093 Q_ASSERT(targetDeclarativeData);
1094 targetDeclarativeData->setPendingBindingBit(bindingTarget, bindingProperty->coreIndex());
1095 }
1096 }
1097
1098 return true;
1099 };
1100 if (!assignBinding(sharedState.data()))
1101 pendingAliasBindings.push_back(assignBinding);
1102 }
1103 return true;
1104 }
1105
1106 if (bindingType == QV4::CompiledData::Binding::Type_Object) {
1107 if (bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) {
1108 // ### determine value source and interceptor casts ahead of time.
1109 QQmlType type = qmlTypeForObject(createdSubObject);
1110 Q_ASSERT(type.isValid());
1111
1112 int valueSourceCast = type.propertyValueSourceCast();
1113 if (valueSourceCast != -1) {
1114 QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast);
1115 QObject *target = createdSubObject->parent();
1116 QQmlProperty prop;
1117 if (_valueTypeProperty) {
1118 prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty,
1119 bindingProperty, context);
1120 } else {
1121 prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context);
1122 }
1123 vs->setTarget(prop);
1124 return true;
1125 }
1126 int valueInterceptorCast = type.propertyValueInterceptorCast();
1127 if (valueInterceptorCast != -1) {
1128 QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast);
1129 QObject *target = createdSubObject->parent();
1130
1131 QQmlPropertyIndex propertyIndex;
1132 if (bindingProperty->isAlias()) {
1133 QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1);
1134 auto aliasTarget = QQmlPropertyPrivate::findAliasTarget(target, originalIndex);
1135 target = aliasTarget.targetObject;
1136 QQmlData *data = QQmlData::get(target);
1137 if (!data || !data->propertyCache) {
1138 qWarning() << "can't resolve property alias for 'on' assignment";
1139 return false;
1140 }
1141
1142 // we can't have aliasses on subproperties of value types, so:
1143 QQmlPropertyData targetPropertyData = *data->propertyCache->property(aliasTarget.targetIndex.coreIndex());
1144 auto prop = QQmlPropertyPrivate::restore(
1145 target, targetPropertyData, nullptr, context);
1146 vi->setTarget(prop);
1147 propertyIndex = QQmlPropertyPrivate::propertyIndex(prop);
1148 } else {
1149 QQmlProperty prop;
1150 if (_valueTypeProperty) {
1151 prop = QQmlPropertyPrivate::restore(
1152 target, *_valueTypeProperty, bindingProperty, context);
1153 } else {
1154 prop = QQmlPropertyPrivate::restore(
1155 target, *bindingProperty, nullptr, context);
1156 }
1157 vi->setTarget(prop);
1158 propertyIndex = QQmlPropertyPrivate::propertyIndex(prop);
1159 }
1160
1161 QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target);
1162 if (!mo)
1163 mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache);
1164 mo->registerInterceptor(propertyIndex, vi);
1165 return true;
1166 }
1167 return false;
1168 }
1169
1170 // Assigning object to signal property? ### Qt 7: Remove that functionality
1171 if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
1172 if (!bindingProperty->isFunction()) {
1173 recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject)));
1174 return false;
1175 }
1176 QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject);
1177 if (!method.isValid()) {
1178 recordError(binding->valueLocation, tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(createdSubObject->metaObject()->className())));
1179 return false;
1180 }
1181 qCWarning(lcQmlDefaultMethod) << "Assigning an object to a signal handler is deprecated. "
1182 "Instead, create the object, give it an id, and call the desired slot "
1183 "from the signal handler. The object is:" << createdSubObject;
1184
1185 QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex());
1186 if (!QMetaObject::checkConnectArgs(signalMethod, method)) {
1187 recordError(binding->valueLocation,
1188 tr("Cannot connect mismatched signal/slot %1 vs %2")
1189 .arg(QString::fromUtf8(method.methodSignature()))
1190 .arg(QString::fromUtf8(signalMethod.methodSignature())));
1191 return false;
1192 }
1193
1194 QQmlPropertyPrivate::connect(_qobject, bindingProperty->coreIndex(), createdSubObject, method.methodIndex());
1195 return true;
1196 }
1197
1198 QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
1199 QQmlPropertyData::RemoveBindingOnAliasWrite;
1200 int propertyWriteStatus = -1;
1201 void *argv[] = { nullptr, nullptr, &propertyWriteStatus, &propertyWriteFlags };
1202
1203 if (const char *iid = QQmlMetaType::interfaceIId(bindingProperty->propType())) {
1204 void *ptr = createdSubObject->qt_metacast(iid);
1205 if (ptr) {
1206 argv[0] = &ptr;
1207 QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv);
1208 } else {
1209 recordError(binding->location, tr("Cannot assign object to interface property"));
1210 return false;
1211 }
1212 } else if (bindingProperty->propType() == QMetaType::fromType<QVariant>()) {
1213 if (bindingProperty->isVarProperty()) {
1214 QV4::Scope scope(v4);
1215 QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject));
1216 _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject);
1217 } else {
1218 QVariant value = QVariant::fromValue(createdSubObject);
1219 argv[0] = &value;
1220 QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv);
1221 }
1222 } else if (bindingProperty->propType() == QMetaType::fromType<QJSValue>()) {
1223 QV4::Scope scope(v4);
1224 QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject));
1225 if (bindingProperty->isVarProperty()) {
1226 _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject);
1227 } else {
1228 QJSValue value;
1229 QJSValuePrivate::setValue(&value, wrappedObject);
1230 argv[0] = &value;
1231 QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv);
1232 }
1233 } else if (bindingProperty->propType().flags().testFlag(QMetaType::IsQmlList)) {
1234 Q_ASSERT(_currentList.object);
1235
1236 QObject *itemToAdd = createdSubObject;
1237
1238 QMetaType listItemType = QQmlMetaType::listValueType(bindingProperty->propType());
1239 if (listItemType.isValid()) {
1240 const char *iid = QQmlMetaType::interfaceIId(listItemType);
1241 if (iid)
1242 itemToAdd = static_cast<QObject *>(createdSubObject->qt_metacast(iid));
1243 }
1244
1245 if (_currentList.append)
1246 _currentList.append(&_currentList, itemToAdd);
1247 else {
1248 recordError(binding->location, tr("Cannot assign object to read only list"));
1249 return false;
1250 }
1251 } else if (bindingProperty->propType().flags().testFlag(QMetaType::PointerToQObject)) {
1252 // pointer compatibility was tested in QQmlPropertyValidator at type compile time
1253 argv[0] = &createdSubObject;
1254 QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv);
1255 } else {
1256 QVariant target = QQmlValueTypeProvider::createValueType(
1257 QVariant::fromValue(createdSubObject), bindingProperty->propType(), v4);
1258 if (target.isValid())
1259 bindingProperty->writeProperty(_qobject, target.data(), propertyWriteFlags);
1260 else
1261 recordError(binding->location, tr("Cannot construct value type from given object"));
1262 }
1263 return true;
1264 }
1265
1266 if (bindingProperty->isQList()) {
1267 recordError(binding->location, tr("Cannot assign primitives to lists"));
1268 return false;
1269 }
1270
1271 setPropertyValue(bindingProperty, binding);
1272 return true;
1273}
1274
1275void QQmlObjectCreator::setupFunctions()
1276{
1277 QV4::Scope scope(v4);
1278 QV4::ScopedValue function(scope);
1279 QV4::ScopedContext qmlContext(scope, currentQmlContext());
1280
1281 const quint32_le *functionIdx = _compiledObject->functionOffsetTable();
1282 for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) {
1283 QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx];
1284 const QString name = runtimeFunction->name()->toQString();
1285
1286 const QQmlPropertyData *property = _propertyCache->property(name, _qobject, context);
1287 if (!property->isVMEFunction())
1288 continue;
1289
1290 if (runtimeFunction->isGenerator())
1291 function = QV4::GeneratorFunction::create(qmlContext, runtimeFunction);
1292 else
1293 function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
1294 _vmeMetaObject->setVmeMethod(property->coreIndex(), function);
1295 }
1296}
1297
1298void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, const QString &description)
1299{
1300 QQmlError error;
1301 error.setUrl(compilationUnit->url());
1302 error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
1303 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
1304 error.setDescription(description);
1305 errors << error;
1306}
1307
1308void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const
1309{
1310 if (object->objectId() >= 0)
1311 context->setIdValue(object->objectId(), instance);
1312}
1313
1314QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject)
1315{
1316 const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
1317 Q_TRACE(QQmlObjectCreator_createInstance_entry, compilationUnit.data(), obj, context->url());
1318 QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj);
1319
1320
1321 const InitFlags flags = (isContextObject ? InitFlag::IsContextObject : InitFlag::None)
1322 | (index == 0 ? InitFlag::IsDocumentRoot : InitFlag::None);
1323
1324 if (obj->hasFlag(QV4::CompiledData::Object::IsComponent)) {
1325 Q_TRACE_EXIT(QQmlObjectCreator_createInstance_exit, QStringLiteral("<component>"));
1326 Q_QML_OC_PROFILE(
1327 sharedState->profiler,
1328 profiler.update(
1329 compilationUnit.data(), obj, QStringLiteral("<component>"),
1330 context->url()));
1331 return initializeComponent(
1332 obj, createComponent(engine, compilationUnit.data(), index, parent, context),
1333 flags);
1334 }
1335
1336 const QV4::ResolvedTypeReference *typeRef = resolvedType(obj->inheritedTypeNameIndex);
1337 Q_ASSERT(typeRef);
1338 const QQmlType type = typeRef->type();
1339 Q_ASSERT(type.isValid());
1340
1341 Q_TRACE_EXIT(QQmlObjectCreator_createInstance_exit, type.qmlTypeName());
1342 Q_QML_OC_PROFILE(
1343 sharedState->profiler,
1344 profiler.update(compilationUnit.data(), obj, type.qmlTypeName(), context->url()));
1345
1346 if (type.isCompositeSingleton()) {
1347 recordError(
1348 obj->location,
1349 tr("Composite Singleton Type %1 is not creatable")
1350 .arg(stringAt(obj->inheritedTypeNameIndex)));
1351 return nullptr;
1352 }
1353
1354 if (!type.isComposite() && !type.isInlineComponentType()) {
1355 if (QObject *instance = type.createWithQQmlData())
1356 return initializeNonComposite(index, obj, typeRef, instance, parent, flags);
1357 recordError(
1358 obj->location,
1359 tr("Unable to create object of type %1").arg(
1360 stringAt(obj->inheritedTypeNameIndex)));
1361 return nullptr;
1362 }
1363
1364 QQmlRefPointer<QV4::ExecutableCompilationUnit> executableCu = typeRef->isSelfReference()
1365 ? compilationUnit
1366 : engine->handle()->executableCompilationUnit(typeRef->compilationUnit());
1367 Q_ASSERT(executableCu);
1368
1369 const bool isInlineComponent = type.isInlineComponentType();
1370 const QString inlineComponentName = isInlineComponent ? type.elementName() : QString();
1371 QQmlObjectCreator subCreator(
1372 context, executableCu, inlineComponentName, sharedState.data(),
1373 isContextObject);
1374
1375 if (QObject *instance = isInlineComponent
1376 ? subCreator.create(
1377 executableCu->inlineComponentId(inlineComponentName), nullptr, nullptr,
1378 CreationFlags::InlineComponent)
1379 : subCreator.create()) {
1380 return initializeComposite(index, obj, typeRef, instance, parent, flags);
1381 }
1382
1383 errors += subCreator.errors;
1384 return nullptr;
1385}
1386
1387void QQmlObjectCreator::initializeDData(
1388 const QV4::CompiledData::Object *obj, QObject *instance, QQmlData *ddata, InitFlags flags)
1389{
1390 ddata->lineNumber = obj->location.line();
1391 ddata->columnNumber = obj->location.column();
1392 ddata->setImplicitDestructible();
1393
1394 // inline components are root objects, but their index is != 0, so we need
1395 // an additional check
1396 const bool documentRoot = (flags & InitFlag::IsDocumentRoot)
1397 || ddata->rootObjectInCreation
1398 || obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot);
1399
1400 context->installContext(
1401 ddata, documentRoot ? QQmlContextData::DocumentRoot : QQmlContextData::OrdinaryObject);
1402
1403 // Register the context object in the context early on in order for pending binding
1404 // initialization to find it available.
1405 if (flags & InitFlag::IsContextObject)
1406 context->setContextObject(instance);
1407}
1408
1409void QQmlObjectCreator::initializePropertyCache(
1410 int index, QQmlData *ddata, const QV4::ResolvedTypeReference *typeRef)
1411{
1412 if (!typeRef->isFullyDynamicType()) {
1413 QQmlPropertyCache::ConstPtr cache = propertyCaches->at(index);
1414 Q_ASSERT(!cache.isNull());
1415 ddata->propertyCache = std::move(cache);
1416 }
1417}
1418
1419void QQmlObjectCreator::initializeParent(QObject *instance, QObject *parent)
1420{
1421 if (instance->isWidgetType()) {
1422 if (parent && parent->isWidgetType()) {
1423 QAbstractDeclarativeData::setWidgetParent(instance, parent);
1424 } else {
1425 // No parent! Layouts need to handle this through a default property that
1426 // reparents accordingly. Otherwise the garbage collector will collect.
1427 }
1428 } else if (parent) {
1429 QQml_setParent_noEvent(instance, parent);
1430 }
1431}
1432
1433QObject *QQmlObjectCreator::populateInstanceAndAliasBindings(
1434 int index, QObject *instance, InitFlags flags)
1435{
1436 const auto doPopulate = [&]() {
1437 if (!populateInstance(
1438 index, instance, /*binding target*/instance, /*value type property*/nullptr)) {
1439 // an error occurred, so we can't setup the pending alias bindings
1440 pendingAliasBindings.clear();
1441 return false;
1442 }
1443
1444 if (!flags.testFlag(InitFlag::IsContextObject))
1445 return true;
1446
1447 while (!pendingAliasBindings.empty()) {
1448 for (std::vector<PendingAliasBinding>::iterator it = pendingAliasBindings.begin();
1449 it != pendingAliasBindings.end(); ) {
1450 if ((*it)(sharedState.data()))
1451 it = pendingAliasBindings.erase(it);
1452 else
1453 ++it;
1454 }
1455 }
1456
1457 return true;
1458 };
1459
1460 QObject *scopeObject = instance;
1461 qSwap(_scopeObject, scopeObject);
1462
1463 Q_ASSERT(sharedState->allJavaScriptObjects.canTrack());
1464 sharedState->allJavaScriptObjects.trackObject(v4, instance);
1465
1466 QV4::Scope valueScope(v4);
1467 QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.constructUndefined(1));
1468
1469 qSwap(_qmlContext, qmlContext);
1470
1471 const bool ok = doPopulate();
1472
1473 qSwap(_qmlContext, qmlContext);
1474 qSwap(_scopeObject, scopeObject);
1475
1476 return ok ? instance : nullptr;
1477}
1478
1479QObject *QQmlObjectCreator::initializeComponent(
1480 const QV4::CompiledData::Object *obj, QObject *instance, InitFlags flags)
1481{
1482 Q_ASSERT(instance);
1483 Q_ASSERT(obj);
1484 Q_ASSERT(obj->hasFlag(QV4::CompiledData::Object::IsComponent));
1485
1486 QScopedValueRollback<QQmlObjectCreator*> ocRestore(
1487 QQmlEnginePrivate::get(engine)->activeObjectCreator, this);
1488
1489 // we just created it inside createComponent; so QQmlData::get() without create = true;
1490 initializeDData(obj, instance, QQmlData::get(instance), flags);
1491
1492 registerObjectWithContextById(obj, instance);
1493 return instance;
1494}
1495
1496QObject *QQmlObjectCreator::initializeComposite(
1497 int index, const QV4::CompiledData::Object *obj, const QV4::ResolvedTypeReference *typeRef,
1498 QObject *instance, QObject *parent, InitFlags flags)
1499{
1500 Q_ASSERT(instance);
1501 Q_ASSERT(typeRef);
1502
1503 QScopedValueRollback<QQmlObjectCreator*> ocRestore(
1504 QQmlEnginePrivate::get(engine)->activeObjectCreator, this);
1505
1506 initializeParent(instance, parent);
1507 QQmlData *ddata = QQmlData::get(instance, /*create*/true);
1508 initializeDData(obj, instance, ddata, flags);
1509
1510 initializePropertyCache(index, ddata, typeRef);
1511
1512 return populateInstanceAndAliasBindings(index, instance, flags);
1513}
1514
1515QObject *QQmlObjectCreator::initializeNonComposite(
1516 int index, const QV4::CompiledData::Object *obj, const QV4::ResolvedTypeReference *typeRef,
1517 QObject *instance, QObject *parent, InitFlags flags)
1518{
1519 Q_ASSERT(instance);
1520 Q_ASSERT(obj);
1521 Q_ASSERT(!obj->hasFlag(QV4::CompiledData::Object::IsComponent));
1522 Q_ASSERT(typeRef);
1523
1524 const QQmlType type = typeRef->type();
1525 Q_ASSERT(type.isValid());
1526 Q_ASSERT(!type.isComposite() && !type.isInlineComponentType());
1527
1528 QScopedValueRollback<QQmlObjectCreator*> ocRestore(QQmlEnginePrivate::get(engine)->activeObjectCreator, this);
1529
1530 if (const int finalizerCast = type.finalizerCast(); finalizerCast != -1) {
1531 const auto hook = reinterpret_cast<QQmlFinalizerHook *>(
1532 reinterpret_cast<char *>(instance) + finalizerCast);
1533 sharedState->finalizeHooks.push_back(hook);
1534 }
1535
1536 QQmlData *ddata = QQmlData::get(instance, /*create*/true);
1537 if (sharedState->rootContext && sharedState->rootContext->isRootObjectInCreation()) {
1538 ddata->rootObjectInCreation = true;
1539 sharedState->rootContext->setRootObjectInCreation(false);
1540 }
1541
1542 const size_t instanceIndex = sharedState->allCreatedObjects.size();
1543 sharedState->allCreatedObjects.push_back(instance);
1544
1545 initializeParent(instance, parent);
1546 initializeDData(obj, instance, ddata, flags);
1547
1548 if (const int parserStatusCast = type.parserStatusCast(); parserStatusCast != -1) {
1549 if (QQmlParserStatus *parserStatus = reinterpret_cast<QQmlParserStatus*>(
1550 reinterpret_cast<char *>(instance) + parserStatusCast)) {
1551 parserStatus->classBegin();
1552 // push() the profiler state here, together with the parserStatus, as we'll pop() them
1553 // together, too.
1554 Q_QML_OC_PROFILE(sharedState->profiler, sharedState->profiler.push(obj));
1555 sharedState->allParserStatusCallbacks.push_back({ instanceIndex, parserStatusCast });
1556 }
1557 }
1558
1559 if (QQmlCustomParser *customParser = type.customParser();
1560 customParser && obj->hasFlag(QV4::CompiledData::Object::HasCustomParserBindings)) {
1561 customParser->engine = QQmlEnginePrivate::get(engine);
1562 customParser->imports = compilationUnit->typeNameCache().data();
1563
1564 QList<const QV4::CompiledData::Binding *> bindings;
1565 const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
1566 const QV4::CompiledData::Binding *binding = obj->bindingTable();
1567 for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
1568 if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
1569 bindings << binding;
1570 }
1571 customParser->applyBindings(instance, compilationUnit, bindings);
1572
1573 customParser->engine = nullptr;
1574 customParser->imports = (QQmlTypeNameCache*)nullptr;
1575 }
1576
1577 initializePropertyCache(index, ddata, typeRef);
1578 return populateInstanceAndAliasBindings(index, instance, flags);
1579}
1580
1581bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
1582{
1583 Q_ASSERT(phase == ObjectsCreated || phase == Finalizing);
1584 phase = Finalizing;
1585
1586 QQmlObjectCreatorRecursionWatcher watcher(this);
1587 QScopedValueRollback<QQmlObjectCreator*> ocRestore(QQmlEnginePrivate::get(engine)->activeObjectCreator, this);
1588
1589 /* We install all pending bindings (both plain QML and QProperty), and remove the ones which do not
1590 actually have dependencies.
1591 It is necessary to install the binding so that it runs at least once, which causes it to capture any
1592 dependencies.
1593 We then check for the following conditions:
1594 - Is the binding in an error state?
1595 - Does the binding has any dependencies (from properties)?
1596 - Does it depend on anything in the context, which has not been resolved yet (and thus couldn't be
1597 captured)?
1598 If the answer to all of those questions is "no", it is safe to remove the binding, as there is no
1599 way for it to change its value afterwards from that point on.
1600 */
1601
1602 while (!sharedState->allCreatedBindings.empty()) {
1603 QQmlAbstractBinding::Ptr b = sharedState->allCreatedBindings.back();
1604 sharedState->allCreatedBindings.pop_back();
1605 Q_ASSERT(b);
1606 // skip, if b is not added to an object
1607 if (!b->isAddedToObject())
1608 continue;
1609 QQmlData *data = QQmlData::get(b->targetObject());
1610 Q_ASSERT(data);
1611 data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex());
1612 b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
1613 QQmlPropertyData::DontRemoveBinding);
1614 if (b->kind() == QQmlAbstractBinding::QmlBinding) {
1615 QQmlBinding *binding = static_cast<QQmlBinding*>(b.data());
1616 if (!binding->hasError() && !binding->hasDependencies()
1617 && !binding->hasUnresolvedNames()) {
1618 b->removeFromObject();
1619 }
1620 }
1621
1622 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1623 return false;
1624 }
1625
1626 while (!sharedState->allQPropertyBindings.isEmpty()) {
1627 auto& [target, index, qmlBinding] = sharedState->allQPropertyBindings.first();
1628
1629 QQmlData *data = QQmlData::get(target);
1630 if (!data || !data->hasBindingBit(index)) {
1631 // The target property has been overwritten since we stashed the binding.
1632 sharedState->allQPropertyBindings.pop_front();
1633 continue;
1634 }
1635
1636 QUntypedBindable bindable;
1637 void *argv[] = { &bindable };
1638 // allow interception
1639 target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
1640 const bool success = bindable.setBinding(qmlBinding);
1641
1642 const auto bindingPrivateRefCount = QPropertyBindingPrivate::get(qmlBinding)->refCount();
1643
1644 // Only pop_front after setting the binding as the bindings are refcounted.
1645 sharedState->allQPropertyBindings.pop_front();
1646
1647 // If the binding was actually not set, it's deleted now.
1648 if (success && bindingPrivateRefCount > 1) {
1649 if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
1650 auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
1651 auto jsExpression = qmlBindingPriv->jsExpression();
1652 const bool canRemove = !qmlBinding.error().hasError()
1653 && !qmlBindingPriv->hasDependencies()
1654 && !jsExpression->hasUnresolvedNames();
1655 if (canRemove)
1656 bindable.takeBinding();
1657 }
1658 }
1659
1660 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1661 return false;
1662 }
1663
1664 if (QQmlVME::componentCompleteEnabled()) { // the qml designer does the component complete later
1665 while (!sharedState->allParserStatusCallbacks.empty()) {
1666 QQmlObjectCompletionProfiler profiler(&sharedState->profiler);
1667 const ParserStatus status = sharedState->allParserStatusCallbacks.back();
1668 sharedState->allParserStatusCallbacks.pop_back();
1669
1670 const QQmlGuard<QObject> &instance = sharedState->allCreatedObjects[status.objectIndex];
1671 if (!instance)
1672 continue;
1673
1674 QQmlParserStatus *parserStatus
1675 = reinterpret_cast<QQmlParserStatus *>(
1676 reinterpret_cast<char *>(instance.data()) + status.parserStatusCast);
1677 parserStatus->componentComplete();
1678
1679 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1680 return false;
1681 }
1682 }
1683
1684 for (QQmlFinalizerHook *hook: sharedState->finalizeHooks) {
1685 hook->componentFinalized();
1686 if (watcher.hasRecursed())
1687 return false;
1688 }
1689 sharedState->finalizeHooks.clear();
1690
1691 while (sharedState->componentAttached) {
1692 QQmlComponentAttached *a = sharedState->componentAttached;
1693 a->removeFromList();
1694 QQmlData *d = QQmlData::get(a->parent());
1695 Q_ASSERT(d);
1696 Q_ASSERT(d->context);
1697 d->context->addComponentAttached(a);
1698 if (QQmlVME::componentCompleteEnabled())
1699 emit a->completed();
1700
1701 if (watcher.hasRecursed() || interrupt.shouldInterrupt())
1702 return false;
1703 }
1704
1705 phase = Done;
1706
1707 return true;
1708}
1709
1710void QQmlObjectCreator::clear()
1711{
1712 if (phase == Done || phase == Finalizing || phase == Startup)
1713 return;
1714 Q_ASSERT(phase != Startup);
1715
1716 while (!sharedState->allCreatedObjects.empty()) {
1717 auto object = sharedState->allCreatedObjects.back();
1718 sharedState->allCreatedObjects.pop_back();
1719 if (engine->objectOwnership(object) != QQmlEngine::CppOwnership) {
1720 delete object;
1721 }
1722 }
1723
1724 while (sharedState->componentAttached) {
1725 QQmlComponentAttached *a = sharedState->componentAttached;
1726 a->removeFromList();
1727 }
1728
1729 phase = Done;
1730}
1731
1732void QQmlObjectCreator::registerPostHocRequiredProperties(const QV4::CompiledData::Binding *binding)
1733{
1734 const qsizetype oldRequiredPropertiesCount = sharedState->requiredProperties.size();
1735 QSet<QString> postHocRequired;
1736 for (auto it = _compiledObject->requiredPropertyExtraDataBegin(); it != _compiledObject->requiredPropertyExtraDataEnd(); ++it)
1737 postHocRequired.insert(stringAt(it->nameIndex));
1738 bool hadInheritedRequiredProperties = !postHocRequired.empty();
1739
1740 for (int propertyIndex = 0; propertyIndex != _compiledObject->propertyCount(); ++propertyIndex) {
1741 const QV4::CompiledData::Property* property = _compiledObject->propertiesBegin() + propertyIndex;
1742 const QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex);
1743 // only compute stringAt if there's a chance for the lookup to succeed
1744 auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(stringAt(property->nameIndex()));
1745 if (!property->isRequired() && postHocRequired.end() == postHocIt)
1746 continue;
1747 if (postHocIt != postHocRequired.end())
1748 postHocRequired.erase(postHocIt);
1749 if (isContextObject)
1750 sharedState->hadTopLevelRequiredProperties = true;
1751 sharedState->requiredProperties.insert({_qobject, propertyData},
1752 RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex()), compilationUnit->finalUrl(), property->location, {}});
1753
1754 }
1755
1756 const auto getPropertyCacheRange = [&]() -> std::pair<int, int> {
1757 // the logic in a nutshell: we work with QML instances here. every
1758 // instance has a QQmlType:
1759 // * if QQmlType is valid && not an inline component, it's a C++ type
1760 // * otherwise, it's a QML-defined type (a.k.a. Composite type), where
1761 // invalid type == "comes from another QML document"
1762 //
1763 // 1. if the type we inherit from comes from C++, we must check *all*
1764 // properties in the property cache so far - since we can have
1765 // required properties defined in C++
1766 // 2. otherwise - the type comes from QML, it's enough to check just
1767 // *own* properties in the property cache, because there's a previous
1768 // type in the hierarchy that has checked the C++ properties (via 1.)
1769 // 3. required attached properties are explicitly not supported. to
1770 // achieve that, go through all its properties
1771 // 4. required group properties: the group itself is covered by 1.
1772 // required sub-properties are not properly handled (QTBUG-96544), so
1773 // just return the old range here for consistency
1774 QV4::ResolvedTypeReference *typeRef = resolvedType(_compiledObject->inheritedTypeNameIndex);
1775 if (!typeRef) { // inside a binding on attached/group property
1776 Q_ASSERT(binding);
1777 if (binding->isAttachedProperty())
1778 return { 0, _propertyCache->propertyCount() }; // 3.
1779 Q_ASSERT(binding->isGroupProperty());
1780 return { 0, _propertyCache->propertyOffset() + 1 }; // 4.
1781 }
1782 Q_ASSERT(!_compiledObject->hasFlag(QV4::CompiledData::Object::IsComponent));
1783 QQmlType type = typeRef->type();
1784 if (type.isComposite() || type.isInlineComponentType())
1785 return { _propertyCache->propertyOffset(), _propertyCache->propertyCount() }; // 2.
1786 return { 0, _propertyCache->propertyCount() }; // 1.
1787 };
1788 const auto [offset, count] = getPropertyCacheRange();
1789 for (int i = offset; i < count; ++i) {
1790 const QQmlPropertyData *propertyData = _propertyCache->maybeUnresolvedProperty(i);
1791 if (!propertyData)
1792 continue;
1793 // TODO: the property might be a group property (in which case we need
1794 // to dive into its sub-properties and check whether there are any
1795 // required elements there) - QTBUG-96544
1796 if (!propertyData->isRequired() && postHocRequired.isEmpty())
1797 continue;
1798 QString name = propertyData->name(_qobject);
1799 const auto postHocIt = postHocRequired.constFind(name);
1800 if (postHocIt == postHocRequired.constEnd()) {
1801 if (!propertyData->isRequired())
1802 continue;
1803 } else {
1804 postHocRequired.erase(postHocIt);
1805 }
1806
1807 if (isContextObject)
1808 sharedState->hadTopLevelRequiredProperties = true;
1809 sharedState->requiredProperties.insert(
1810 {_qobject, propertyData},
1811 RequiredPropertyInfo {
1812 std::move(name), compilationUnit->finalUrl(), _compiledObject->location, {}
1813 });
1814 }
1815
1816 if (binding && binding->isAttachedProperty()
1817 && sharedState->requiredProperties.size() != oldRequiredPropertiesCount) {
1818 recordError(
1819 binding->location,
1820 QLatin1String("Attached property has required properties. This is not supported"));
1821 }
1822
1823 // Note: there's a subtle case with the above logic: if we process a random
1824 // QML-defined leaf type, it could have a required attribute overwrite on an
1825 // *existing* property: `import QtQuick; Text { required text }`. in this
1826 // case, we must add the property to a required list
1827 if (!postHocRequired.isEmpty()) {
1828 // NB: go through [0, offset) range as [offset, count) is already done
1829 for (int i = 0; i < offset; ++i) {
1830 const QQmlPropertyData *propertyData = _propertyCache->maybeUnresolvedProperty(i);
1831 if (!propertyData)
1832 continue;
1833 QString name = propertyData->name(_qobject);
1834 const auto postHocIt = postHocRequired.constFind(name);
1835 if (postHocIt == postHocRequired.constEnd())
1836 continue;
1837 postHocRequired.erase(postHocIt);
1838
1839 if (isContextObject)
1840 sharedState->hadTopLevelRequiredProperties = true;
1841 sharedState->requiredProperties.insert(
1842 {_qobject, propertyData},
1843 RequiredPropertyInfo {
1844 std::move(name), compilationUnit->finalUrl(), _compiledObject->location, {}
1845 });
1846 }
1847 }
1848
1849 if (!postHocRequired.isEmpty() && hadInheritedRequiredProperties)
1850 recordError({}, QLatin1String("Property %1 was marked as required but does not exist").arg(*postHocRequired.begin()));
1851}
1852
1853bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget,
1854 const QQmlPropertyData *valueTypeProperty,
1855 const QV4::CompiledData::Binding *binding)
1856{
1857 Q_ASSERT(instance);
1858 QQmlData *declarativeData = QQmlData::get(instance, /*create*/true);
1859
1860 qSwap(_qobject, instance);
1861 qSwap(_valueTypeProperty, valueTypeProperty);
1862 qSwap(_compiledObjectIndex, index);
1863 const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
1864 qSwap(_compiledObject, obj);
1865 qSwap(_ddata, declarativeData);
1866 qSwap(_bindingTarget, bindingTarget);
1867
1868 QV4::Scope valueScope(v4);
1869 QV4::ScopedValue scopeObjectProtector(valueScope);
1870
1871 QQmlPropertyCache::ConstPtr cache = propertyCaches->at(_compiledObjectIndex);
1872
1873 QQmlVMEMetaObject *vmeMetaObject = nullptr;
1874 if (propertyCaches->needsVMEMetaObject(_compiledObjectIndex)) {
1875 Q_ASSERT(!cache.isNull());
1876 // install on _object
1877 vmeMetaObject = new QQmlVMEMetaObject(v4, _qobject, cache, compilationUnit, _compiledObjectIndex);
1878 _ddata->propertyCache = cache;
1879 scopeObjectProtector = _ddata->jsWrapper.value();
1880 } else {
1881 vmeMetaObject = QQmlVMEMetaObject::get(_qobject);
1882 }
1883
1884 registerObjectWithContextById(_compiledObject, _qobject);
1885
1886 qSwap(_propertyCache, cache);
1887 qSwap(_vmeMetaObject, vmeMetaObject);
1888
1889 _ddata->compilationUnit = compilationUnit;
1890 if (_compiledObject->hasFlag(QV4::CompiledData::Object::HasDeferredBindings))
1891 _ddata->deferData(_compiledObjectIndex, compilationUnit, context, m_inlineComponentName);
1892
1893 registerPostHocRequiredProperties(binding);
1894
1895 if (_compiledObject->nFunctions > 0)
1896 setupFunctions();
1897 setupBindings((binding && binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding))
1898 ? BindingMode::ApplyAll
1899 : BindingMode::ApplyImmediate);
1900
1901 for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) {
1902 // Ensure aliasChanged() signals are connected during object creation.
1903 // This is necessary because alias signals may not have been connected
1904 // if the signal handlers are defined and connected in C++ code rather
1905 // than being declared in QML.
1906 _vmeMetaObject->connectAlias(_compiledObject, aliasIndex);
1907 const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex;
1908 const auto originalAlias = alias;
1909 while (alias->isAliasToLocalAlias())
1910 alias = _compiledObject->aliasesBegin() + alias->localAliasIndex;
1911 if (!context->isIdValueSet(0)) // TODO: Do we really want 0 here?
1912 continue;
1913 QObject *target = context->idValue(alias->targetObjectId());
1914 if (!target)
1915 continue;
1916 QQmlData *targetDData = QQmlData::get(target, /*create*/false);
1917 if (targetDData == nullptr || targetDData->propertyCache.isNull())
1918 continue;
1919
1920 const QQmlPropertyData *aliasProperty =
1921 _propertyCache->property(_vmeMetaObject->aliasOffset() + aliasIndex);
1922 if (!aliasProperty)
1923 continue;
1924 const int targetPropertyIndex = aliasProperty->aliasTarget();
1925 int coreIndex = QQmlPropertyIndex::fromEncoded(targetPropertyIndex).coreIndex();
1926
1927 const QQmlPropertyData *const targetProperty = targetDData->propertyCache->property(coreIndex);
1928 if (!targetProperty)
1929 continue;
1930 auto it = sharedState->requiredProperties.find({target, targetProperty});
1931 if (it != sharedState->requiredProperties.end())
1932 it->aliasesToRequired.push_back(
1933 AliasToRequiredInfo {
1934 compilationUnit->stringAt(originalAlias->nameIndex()),
1935 compilationUnit->finalUrl()
1936 });
1937 }
1938
1939 qSwap(_vmeMetaObject, vmeMetaObject);
1940 qSwap(_bindingTarget, bindingTarget);
1941 qSwap(_ddata, declarativeData);
1942 qSwap(_compiledObject, obj);
1943 qSwap(_compiledObjectIndex, index);
1944 qSwap(_valueTypeProperty, valueTypeProperty);
1945 qSwap(_qobject, instance);
1946 qSwap(_propertyCache, cache);
1947
1948 return errors.isEmpty();
1949}
1950
1951/*!
1952 \internal
1953*/
1954QQmlComponent *QQmlObjectCreator::createComponent(
1955 QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit, int index,
1956 QObject *parent, const QQmlRefPointer<QQmlContextData> &context)
1957{
1958 QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent);
1959 QQmlComponentPrivate::get(component)->setCreationContext(context);
1960 QQmlData::get(component, /*create*/ true);
1961 return component;
1962}
1963
1969
1970void ObjectInCreationGCAnchorList::trackObject(QV4::ExecutionEngine *engine, QObject *instance)
1971{
1972 QV4::Value *wrapper = allocationScope->construct(1, QV4::QObjectWrapper::wrap(engine, instance));
1973 // we have to handle the case where the gc is already running, but the scope is discarded
1974 // before the collector runs again. In that case, rescanning won't help us. Thus, mark the
1975 // object.
1976 QV4::WriteBarrier::markCustom(engine, [wrapper](QV4::MarkStack *ms) {
1977 wrapper->heapObject()->mark(ms);
1978 });
1979}
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static QQmlType qmlTypeForObject(QObject *object)
QQmlObjectCreatorRecursionWatcher(QQmlObjectCreator *creator)