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

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