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 | |
37 | Q_LOGGING_CATEGORY(lcQmlDefaultMethod, "qt.qml.defaultmethod" ) |
38 | |
39 | QT_USE_NAMESPACE |
40 | |
41 | Q_TRACE_PREFIX(qtqml, |
42 | "namespace QV4 {" \ |
43 | "struct ExecutionEngine;" \ |
44 | "namespace CompiledData {" \ |
45 | "struct CompilationUnit;" \ |
46 | "struct Object;" \ |
47 | "}}" \ |
48 | "class QQmlEngine;" |
49 | ) |
50 | |
51 | Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_entry, const QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url) |
52 | Q_TRACE_POINT(qtqml, QQmlObjectCreator_createInstance_exit, const QString &typeName) |
53 | |
54 | QQmlObjectCreator::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 | |
86 | QQmlObjectCreator::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 | |
100 | void 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 | |
121 | QQmlObjectCreator::~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 | |
139 | QObject *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 | |
230 | void 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 | |
242 | void 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 | |
268 | void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex) |
269 | { |
270 | doPopulateDeferred(instance, deferredIndex, f: [this]() { setupBindings(ApplyDeferred); }); |
271 | } |
272 | |
273 | bool 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 | |
282 | void 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 | |
293 | void 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 | |
303 | void QQmlObjectCreator::finalizePopulateDeferred() |
304 | { |
305 | phase = ObjectsCreated; |
306 | } |
307 | |
308 | void 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 | |
642 | static 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 | |
653 | void 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 | |
781 | bool 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 | |
1174 | void 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 | |
1197 | void 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 | |
1207 | void 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 | |
1213 | QObject *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 | |
1426 | bool 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 | |
1539 | void 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 | |
1560 | bool 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 | */ |
1765 | QQmlComponent *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 | |
1776 | QQmlObjectCreatorRecursionWatcher::QQmlObjectCreatorRecursionWatcher(QQmlObjectCreator *creator) |
1777 | : sharedState(creator->sharedState) |
1778 | , watcher(creator->sharedState.data()) |
1779 | { |
1780 | } |
1781 | |