1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qqmlobjectcreator_p.h" |
41 | |
42 | #include <private/qqmlengine_p.h> |
43 | #include <private/qqmlvmemetaobject_p.h> |
44 | #include <private/qv4function_p.h> |
45 | #include <private/qv4functionobject_p.h> |
46 | #include <private/qv4qobjectwrapper_p.h> |
47 | #include <private/qqmlbinding_p.h> |
48 | #include <private/qqmlstringconverters_p.h> |
49 | #include <private/qqmlboundsignal_p.h> |
50 | #include <private/qqmlcomponentattached_p.h> |
51 | #include <private/qqmlcomponent_p.h> |
52 | #include <private/qqmlcustomparser_p.h> |
53 | #include <private/qqmlscriptstring_p.h> |
54 | #include <private/qqmlpropertyvalueinterceptor_p.h> |
55 | #include <private/qqmlvaluetypeproxybinding_p.h> |
56 | #include <private/qqmldebugconnector_p.h> |
57 | #include <private/qqmldebugserviceinterfaces_p.h> |
58 | #include <private/qqmlscriptdata_p.h> |
59 | #include <private/qqmlsourcecoordinate_p.h> |
60 | #include <private/qjsvalue_p.h> |
61 | #include <private/qv4generatorobject_p.h> |
62 | |
63 | #include <QScopedValueRollback> |
64 | |
65 | #include <qtqml_tracepoints_p.h> |
66 | #include <QScopedValueRollback> |
67 | |
68 | QT_USE_NAMESPACE |
69 | |
70 | QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, |
71 | QQmlIncubatorPrivate *incubator) |
72 | : phase(Startup) |
73 | , compilationUnit(compilationUnit) |
74 | , propertyCaches(&compilationUnit->propertyCaches) |
75 | , sharedState(new QQmlObjectCreatorSharedState) |
76 | , topLevelCreator(true) |
77 | , incubator(incubator) |
78 | { |
79 | init(parentContext); |
80 | |
81 | sharedState->componentAttached = nullptr; |
82 | sharedState->allCreatedBindings.allocate(size: compilationUnit->totalBindingsCount()); |
83 | sharedState->allParserStatusCallbacks.allocate(size: compilationUnit->totalParserStatusCount()); |
84 | sharedState->allCreatedObjects.allocate(size: compilationUnit->totalObjectCount()); |
85 | sharedState->allJavaScriptObjects = nullptr; |
86 | sharedState->creationContext = creationContext; |
87 | sharedState->rootContext = nullptr; |
88 | sharedState->hadRequiredProperties = false; |
89 | |
90 | if (auto profiler = QQmlEnginePrivate::get(e: engine)->profiler) { |
91 | Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, |
92 | sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount())); |
93 | } else { |
94 | Q_UNUSED(profiler); |
95 | } |
96 | } |
97 | |
98 | QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) |
99 | : phase(Startup) |
100 | , compilationUnit(compilationUnit) |
101 | , propertyCaches(&compilationUnit->propertyCaches) |
102 | , sharedState(inheritedSharedState) |
103 | , topLevelCreator(false) |
104 | , incubator(nullptr) |
105 | { |
106 | init(parentContext); |
107 | } |
108 | |
109 | void QQmlObjectCreator::init(QQmlContextData *providedParentContext) |
110 | { |
111 | parentContext = providedParentContext; |
112 | engine = parentContext->engine; |
113 | v4 = engine->handle(); |
114 | |
115 | if (compilationUnit && !compilationUnit->engine) |
116 | compilationUnit->linkToEngine(engine: v4); |
117 | |
118 | qmlUnit = compilationUnit->unitData(); |
119 | context = nullptr; |
120 | _qobject = nullptr; |
121 | _scopeObject = nullptr; |
122 | _bindingTarget = nullptr; |
123 | _valueTypeProperty = nullptr; |
124 | _compiledObject = nullptr; |
125 | _compiledObjectIndex = -1; |
126 | _ddata = nullptr; |
127 | _propertyCache = nullptr; |
128 | _vmeMetaObject = nullptr; |
129 | _qmlContext = nullptr; |
130 | } |
131 | |
132 | QQmlObjectCreator::~QQmlObjectCreator() |
133 | { |
134 | if (topLevelCreator) { |
135 | { |
136 | QQmlObjectCreatorRecursionWatcher watcher(this); |
137 | } |
138 | for (int i = 0; i < sharedState->allParserStatusCallbacks.count(); ++i) { |
139 | QQmlParserStatus *ps = sharedState->allParserStatusCallbacks.at(index: i); |
140 | if (ps) |
141 | ps->d = nullptr; |
142 | } |
143 | while (sharedState->componentAttached) { |
144 | QQmlComponentAttached *a = sharedState->componentAttached; |
145 | a->rem(); |
146 | } |
147 | } |
148 | } |
149 | |
150 | QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt, int flags) |
151 | { |
152 | if (phase == CreatingObjectsPhase2) { |
153 | phase = ObjectsCreated; |
154 | return context->contextObject; |
155 | } |
156 | Q_ASSERT(phase == Startup); |
157 | phase = CreatingObjects; |
158 | |
159 | int objectToCreate; |
160 | |
161 | if (subComponentIndex == -1) { |
162 | objectToCreate = /*root object*/0; |
163 | } else { |
164 | Q_ASSERT(subComponentIndex >= 0); |
165 | if (flags & CreationFlags::InlineComponent) { |
166 | objectToCreate = subComponentIndex; |
167 | } else { |
168 | Q_ASSERT(flags & CreationFlags::NormalObject); |
169 | const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(index: subComponentIndex); |
170 | objectToCreate = compObj->bindingTable()->value.objectIndex; |
171 | } |
172 | } |
173 | |
174 | context = new QQmlContextData; |
175 | context->isInternal = true; |
176 | context->imports = compilationUnit->typeNameCache; |
177 | context->initFromTypeCompilationUnit(unit: compilationUnit, subComponentIndex); |
178 | context->setParent(parentContext); |
179 | |
180 | if (!sharedState->rootContext) { |
181 | sharedState->rootContext = context; |
182 | sharedState->rootContext->incubator = incubator; |
183 | sharedState->rootContext->isRootObjectInCreation = true; |
184 | } |
185 | |
186 | QV4::Scope scope(v4); |
187 | |
188 | Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator); |
189 | if (topLevelCreator) |
190 | sharedState->allJavaScriptObjects = scope.alloc(nValues: compilationUnit->totalObjectCount()); |
191 | |
192 | if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) { |
193 | QV4::ScopedObject scripts(scope, v4->newArrayObject(count: compilationUnit->dependentScripts.count())); |
194 | context->importedScripts.set(engine: v4, value: scripts); |
195 | QV4::ScopedValue v(scope); |
196 | for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) { |
197 | QQmlRefPointer<QQmlScriptData> s = compilationUnit->dependentScripts.at(i); |
198 | scripts->put(idx: i, v: (v = s->scriptValueForContext(parentCtxt: context))); |
199 | } |
200 | } else if (sharedState->creationContext) { |
201 | context->importedScripts = sharedState->creationContext->importedScripts; |
202 | } |
203 | |
204 | QObject *instance = createInstance(index: objectToCreate, parent, /*isContextObject*/true); |
205 | if (instance) { |
206 | QQmlData *ddata = QQmlData::get(object: instance); |
207 | Q_ASSERT(ddata); |
208 | ddata->compilationUnit = compilationUnit; |
209 | } |
210 | |
211 | if (topLevelCreator) |
212 | sharedState->allJavaScriptObjects = nullptr; |
213 | |
214 | phase = CreatingObjectsPhase2; |
215 | |
216 | if (interrupt && interrupt->shouldInterrupt()) |
217 | return nullptr; |
218 | |
219 | phase = ObjectsCreated; |
220 | |
221 | if (instance) { |
222 | if (QQmlEngineDebugService *service |
223 | = QQmlDebugConnector::service<QQmlEngineDebugService>()) { |
224 | if (!parentContext->isInternal) |
225 | parentContext->asQQmlContextPrivate()->instances.append(t: instance); |
226 | service->objectCreated(engine, object: instance); |
227 | } else if (!parentContext->isInternal && QQmlDebugConnector::service<QV4DebugService>()) { |
228 | parentContext->asQQmlContextPrivate()->instances.append(t: instance); |
229 | } |
230 | } |
231 | |
232 | return instance; |
233 | } |
234 | |
235 | void QQmlObjectCreator::beginPopulateDeferred(QQmlContextData *newContext) |
236 | { |
237 | context = newContext; |
238 | sharedState->rootContext = newContext; |
239 | |
240 | Q_ASSERT(topLevelCreator); |
241 | Q_ASSERT(!sharedState->allJavaScriptObjects); |
242 | |
243 | QV4::Scope valueScope(v4); |
244 | sharedState->allJavaScriptObjects = valueScope.alloc(nValues: compilationUnit->totalObjectCount()); |
245 | } |
246 | |
247 | void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex, |
248 | const QQmlPropertyPrivate *qmlProperty, |
249 | const QV4::CompiledData::Binding *binding) |
250 | { |
251 | QQmlData *declarativeData = QQmlData::get(object: instance); |
252 | QObject *bindingTarget = instance; |
253 | |
254 | QQmlRefPointer<QQmlPropertyCache> cache = declarativeData->propertyCache; |
255 | QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(obj: instance); |
256 | |
257 | QObject *scopeObject = instance; |
258 | qSwap(value1&: _scopeObject, value2&: scopeObject); |
259 | |
260 | QV4::Scope valueScope(v4); |
261 | QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects, |
262 | valueScope.alloc(nValues: compilationUnit->totalObjectCount())); |
263 | |
264 | Q_ASSERT(topLevelCreator); |
265 | QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); |
266 | |
267 | qSwap(value1&: _qmlContext, value2&: qmlContext); |
268 | |
269 | qSwap(value1&: _propertyCache, value2&: cache); |
270 | qSwap(value1&: _qobject, value2&: instance); |
271 | |
272 | int objectIndex = deferredIndex; |
273 | qSwap(value1&: _compiledObjectIndex, value2&: objectIndex); |
274 | |
275 | const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index: _compiledObjectIndex); |
276 | qSwap(value1&: _compiledObject, value2&: obj); |
277 | qSwap(value1&: _ddata, value2&: declarativeData); |
278 | qSwap(value1&: _bindingTarget, value2&: bindingTarget); |
279 | qSwap(value1&: _vmeMetaObject, value2&: vmeMetaObject); |
280 | |
281 | if (binding) { |
282 | Q_ASSERT(qmlProperty); |
283 | Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding); |
284 | |
285 | QQmlListProperty<void> savedList; |
286 | qSwap(value1&: _currentList, value2&: savedList); |
287 | |
288 | const QQmlPropertyData &property = qmlProperty->core; |
289 | |
290 | if (property.isQList()) { |
291 | void *argv[1] = { (void*)&_currentList }; |
292 | QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property.coreIndex(), argv); |
293 | } else if (_currentList.object) { |
294 | _currentList = QQmlListProperty<void>(); |
295 | } |
296 | |
297 | setPropertyBinding(property: &property, binding); |
298 | |
299 | qSwap(value1&: _currentList, value2&: savedList); |
300 | } else { |
301 | setupBindings(/*applyDeferredBindings=*/true); |
302 | } |
303 | |
304 | qSwap(value1&: _vmeMetaObject, value2&: vmeMetaObject); |
305 | qSwap(value1&: _bindingTarget, value2&: bindingTarget); |
306 | qSwap(value1&: _ddata, value2&: declarativeData); |
307 | qSwap(value1&: _compiledObject, value2&: obj); |
308 | qSwap(value1&: _compiledObjectIndex, value2&: objectIndex); |
309 | qSwap(value1&: _qobject, value2&: instance); |
310 | qSwap(value1&: _propertyCache, value2&: cache); |
311 | |
312 | qSwap(value1&: _qmlContext, value2&: qmlContext); |
313 | qSwap(value1&: _scopeObject, value2&: scopeObject); |
314 | } |
315 | |
316 | bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, |
317 | const QQmlData::DeferredData *deferredData) |
318 | { |
319 | beginPopulateDeferred(newContext: deferredData->context); |
320 | populateDeferred(instance, deferredIndex: deferredData->deferredIdx); |
321 | finalizePopulateDeferred(); |
322 | return errors.isEmpty(); |
323 | } |
324 | |
325 | void QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex, |
326 | const QV4::CompiledData::Binding *binding) |
327 | { |
328 | populateDeferred(instance: qmlProperty.object(), deferredIndex, qmlProperty: QQmlPropertyPrivate::get(p: qmlProperty), |
329 | binding); |
330 | } |
331 | |
332 | void QQmlObjectCreator::finalizePopulateDeferred() |
333 | { |
334 | phase = ObjectsCreated; |
335 | } |
336 | |
337 | void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) |
338 | { |
339 | QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; |
340 | QV4::Scope scope(v4); |
341 | |
342 | int propertyType = property->propType(); |
343 | |
344 | if (property->isEnum()) { |
345 | if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) { |
346 | propertyType = QMetaType::Int; |
347 | } else { |
348 | // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. |
349 | QVariant value = compilationUnit->bindingValueAsString(binding); |
350 | bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context); |
351 | Q_ASSERT(ok); |
352 | Q_UNUSED(ok); |
353 | return; |
354 | } |
355 | } |
356 | |
357 | auto assertOrNull = [&](bool ok) |
358 | { |
359 | Q_ASSERT(ok || binding->type == QV4::CompiledData::Binding::Type_Null); |
360 | Q_UNUSED(ok); |
361 | }; |
362 | |
363 | auto assertType = [&](QV4::CompiledData::Binding::ValueType type) |
364 | { |
365 | Q_ASSERT(binding->type == type || binding->type == QV4::CompiledData::Binding::Type_Null); |
366 | Q_UNUSED(type); |
367 | }; |
368 | |
369 | if (property->isQObject()) { |
370 | if (binding->type == QV4::CompiledData::Binding::Type_Null) { |
371 | QObject *value = nullptr; |
372 | const bool ok = property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
373 | Q_ASSERT(ok); |
374 | Q_UNUSED(ok); |
375 | return; |
376 | } |
377 | } |
378 | |
379 | switch (propertyType) { |
380 | case QMetaType::QVariant: { |
381 | if (binding->type == QV4::CompiledData::Binding::Type_Number) { |
382 | double n = compilationUnit->bindingValueAsNumber(binding); |
383 | if (double(int(n)) == n) { |
384 | if (property->isVarProperty()) { |
385 | _vmeMetaObject->setVMEProperty(index: property->coreIndex(), v: QV4::Value::fromInt32(i: int(n))); |
386 | } else { |
387 | int i = int(n); |
388 | QVariant value(i); |
389 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
390 | } |
391 | } else { |
392 | if (property->isVarProperty()) { |
393 | _vmeMetaObject->setVMEProperty(index: property->coreIndex(), v: QV4::Value::fromDouble(d: n)); |
394 | } else { |
395 | QVariant value(n); |
396 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
397 | } |
398 | } |
399 | } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { |
400 | if (property->isVarProperty()) { |
401 | _vmeMetaObject->setVMEProperty(index: property->coreIndex(), v: QV4::Value::fromBoolean(b: binding->valueAsBoolean())); |
402 | } else { |
403 | QVariant value(binding->valueAsBoolean()); |
404 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
405 | } |
406 | } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { |
407 | if (property->isVarProperty()) { |
408 | _vmeMetaObject->setVMEProperty(index: property->coreIndex(), v: QV4::Value::nullValue()); |
409 | } else { |
410 | QVariant nullValue = QVariant::fromValue(value: nullptr); |
411 | property->writeProperty(target: _qobject, value: &nullValue, flags: propertyWriteFlags); |
412 | } |
413 | } else { |
414 | QString stringValue = compilationUnit->bindingValueAsString(binding); |
415 | if (property->isVarProperty()) { |
416 | QV4::ScopedString s(scope, v4->newString(s: stringValue)); |
417 | _vmeMetaObject->setVMEProperty(index: property->coreIndex(), v: s); |
418 | } else { |
419 | // ### Qt 6: Doing the conversion here where we don't know the eventual target type is rather strange |
420 | // and caused for instance QTBUG-78943 |
421 | QVariant value = QQmlStringConverters::variantFromString(stringValue); |
422 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
423 | } |
424 | } |
425 | } |
426 | break; |
427 | case QMetaType::QString: { |
428 | assertOrNull(binding->evaluatesToString()); |
429 | QString value = compilationUnit->bindingValueAsString(binding); |
430 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
431 | } |
432 | break; |
433 | case QMetaType::QStringList: { |
434 | assertOrNull(binding->evaluatesToString()); |
435 | QStringList value(compilationUnit->bindingValueAsString(binding)); |
436 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
437 | } |
438 | break; |
439 | case QMetaType::QByteArray: { |
440 | assertType(QV4::CompiledData::Binding::Type_String); |
441 | QByteArray value(compilationUnit->bindingValueAsString(binding).toUtf8()); |
442 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
443 | } |
444 | break; |
445 | case QMetaType::QUrl: { |
446 | assertType(QV4::CompiledData::Binding::Type_String); |
447 | QString string = compilationUnit->bindingValueAsString(binding); |
448 | // Encoded dir-separators defeat QUrl processing - decode them first |
449 | string.replace(before: QLatin1String("%2f" ), after: QLatin1String("/" ), cs: Qt::CaseInsensitive); |
450 | QUrl value = string.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(relative: QUrl(string)); |
451 | // Apply URL interceptor |
452 | if (engine->urlInterceptor()) |
453 | value = engine->urlInterceptor()->intercept(path: value, type: QQmlAbstractUrlInterceptor::UrlString); |
454 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
455 | } |
456 | break; |
457 | case QMetaType::UInt: { |
458 | assertType(QV4::CompiledData::Binding::Type_Number); |
459 | double d = compilationUnit->bindingValueAsNumber(binding); |
460 | uint value = uint(d); |
461 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
462 | break; |
463 | } |
464 | break; |
465 | case QMetaType::Int: { |
466 | assertType(QV4::CompiledData::Binding::Type_Number); |
467 | double d = compilationUnit->bindingValueAsNumber(binding); |
468 | int value = int(d); |
469 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
470 | break; |
471 | } |
472 | break; |
473 | case QMetaType::Float: { |
474 | assertType(QV4::CompiledData::Binding::Type_Number); |
475 | float value = float(compilationUnit->bindingValueAsNumber(binding)); |
476 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
477 | } |
478 | break; |
479 | case QMetaType::Double: { |
480 | assertType(QV4::CompiledData::Binding::Type_Number); |
481 | double value = compilationUnit->bindingValueAsNumber(binding); |
482 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
483 | } |
484 | break; |
485 | case QMetaType::QColor: { |
486 | bool ok = false; |
487 | uint colorValue = QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
488 | assertOrNull(ok); |
489 | struct { void *data[4]; } buffer; |
490 | if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) { |
491 | property->writeProperty(target: _qobject, value: &buffer, flags: propertyWriteFlags); |
492 | } |
493 | } |
494 | break; |
495 | #if QT_CONFIG(datestring) |
496 | case QMetaType::QDate: { |
497 | bool ok = false; |
498 | QDate value = QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
499 | assertOrNull(ok); |
500 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
501 | } |
502 | break; |
503 | case QMetaType::QTime: { |
504 | bool ok = false; |
505 | QTime value = QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
506 | assertOrNull(ok); |
507 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
508 | } |
509 | break; |
510 | case QMetaType::QDateTime: { |
511 | bool ok = false; |
512 | QDateTime value = QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
513 | // ### VME compatibility :( |
514 | { |
515 | const qint64 date = value.date().toJulianDay(); |
516 | const int msecsSinceStartOfDay = value.time().msecsSinceStartOfDay(); |
517 | value = QDateTime(QDate::fromJulianDay(jd_: date), QTime::fromMSecsSinceStartOfDay(msecs: msecsSinceStartOfDay)); |
518 | } |
519 | assertOrNull(ok); |
520 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
521 | } |
522 | break; |
523 | #endif // datestring |
524 | case QMetaType::QPoint: { |
525 | bool ok = false; |
526 | QPoint value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), ok: &ok).toPoint(); |
527 | assertOrNull(ok); |
528 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
529 | } |
530 | break; |
531 | case QMetaType::QPointF: { |
532 | bool ok = false; |
533 | QPointF value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
534 | assertOrNull(ok); |
535 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
536 | } |
537 | break; |
538 | case QMetaType::QSize: { |
539 | bool ok = false; |
540 | QSize value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), ok: &ok).toSize(); |
541 | assertOrNull(ok); |
542 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
543 | } |
544 | break; |
545 | case QMetaType::QSizeF: { |
546 | bool ok = false; |
547 | QSizeF value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
548 | assertOrNull(ok); |
549 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
550 | } |
551 | break; |
552 | case QMetaType::QRect: { |
553 | bool ok = false; |
554 | QRect value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), ok: &ok).toRect(); |
555 | assertOrNull(ok); |
556 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
557 | } |
558 | break; |
559 | case QMetaType::QRectF: { |
560 | bool ok = false; |
561 | QRectF value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), ok: &ok); |
562 | assertOrNull(ok); |
563 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
564 | } |
565 | break; |
566 | case QMetaType::Bool: { |
567 | assertType(QV4::CompiledData::Binding::Type_Boolean); |
568 | bool value = binding->valueAsBoolean(); |
569 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
570 | } |
571 | break; |
572 | case QMetaType::QVector2D: { |
573 | struct { |
574 | float xp; |
575 | float yp; |
576 | } vec; |
577 | bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); |
578 | assertOrNull(ok); |
579 | Q_UNUSED(ok); |
580 | property->writeProperty(target: _qobject, value: &vec, flags: propertyWriteFlags); |
581 | } |
582 | break; |
583 | case QMetaType::QVector3D: { |
584 | struct { |
585 | float xp; |
586 | float yp; |
587 | float zy; |
588 | } vec; |
589 | bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); |
590 | assertOrNull(ok); |
591 | Q_UNUSED(ok); |
592 | property->writeProperty(target: _qobject, value: &vec, flags: propertyWriteFlags); |
593 | } |
594 | break; |
595 | case QMetaType::QVector4D: { |
596 | struct { |
597 | float xp; |
598 | float yp; |
599 | float zy; |
600 | float wp; |
601 | } vec; |
602 | bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); |
603 | assertOrNull(ok); |
604 | Q_UNUSED(ok); |
605 | property->writeProperty(target: _qobject, value: &vec, flags: propertyWriteFlags); |
606 | } |
607 | break; |
608 | case QMetaType::QQuaternion: { |
609 | struct { |
610 | float wp; |
611 | float xp; |
612 | float yp; |
613 | float zp; |
614 | } vec; |
615 | bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); |
616 | assertOrNull(ok); |
617 | Q_UNUSED(ok); |
618 | property->writeProperty(target: _qobject, value: &vec, flags: propertyWriteFlags); |
619 | } |
620 | break; |
621 | case QMetaType::QRegExp: |
622 | assertOrNull(!"not possible" ); |
623 | break; |
624 | default: { |
625 | // generate single literal value assignment to a list property if required |
626 | if (property->propType() == qMetaTypeId<QList<qreal> >()) { |
627 | assertType(QV4::CompiledData::Binding::Type_Number); |
628 | QList<qreal> value; |
629 | value.append(t: compilationUnit->bindingValueAsNumber(binding)); |
630 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
631 | break; |
632 | } else if (property->propType() == qMetaTypeId<QList<int> >()) { |
633 | assertType(QV4::CompiledData::Binding::Type_Number); |
634 | double n = compilationUnit->bindingValueAsNumber(binding); |
635 | QList<int> value; |
636 | value.append(t: int(n)); |
637 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
638 | break; |
639 | } else if (property->propType() == qMetaTypeId<QList<bool> >()) { |
640 | assertType(QV4::CompiledData::Binding::Type_Boolean); |
641 | QList<bool> value; |
642 | value.append(t: binding->valueAsBoolean()); |
643 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
644 | break; |
645 | } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { |
646 | assertType(QV4::CompiledData::Binding::Type_String); |
647 | QString urlString = compilationUnit->bindingValueAsString(binding); |
648 | QUrl u = urlString.isEmpty() ? QUrl() |
649 | : compilationUnit->finalUrl().resolved(relative: QUrl(urlString)); |
650 | QList<QUrl> value; |
651 | value.append(t: u); |
652 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
653 | break; |
654 | } else if (property->propType() == qMetaTypeId<QList<QString> >()) { |
655 | assertOrNull(binding->evaluatesToString()); |
656 | QList<QString> value; |
657 | value.append(t: compilationUnit->bindingValueAsString(binding)); |
658 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
659 | break; |
660 | } else if (property->propType() == qMetaTypeId<QJSValue>()) { |
661 | QJSValue value; |
662 | if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { |
663 | value = QJSValue(binding->valueAsBoolean()); |
664 | } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { |
665 | double n = compilationUnit->bindingValueAsNumber(binding); |
666 | if (double(int(n)) == n) { |
667 | value = QJSValue(int(n)); |
668 | } else |
669 | value = QJSValue(n); |
670 | } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { |
671 | value = QJSValue::NullValue; |
672 | } else { |
673 | value = QJSValue(compilationUnit->bindingValueAsString(binding)); |
674 | } |
675 | property->writeProperty(target: _qobject, value: &value, flags: propertyWriteFlags); |
676 | break; |
677 | } |
678 | |
679 | // otherwise, try a custom type assignment |
680 | QString stringValue = compilationUnit->bindingValueAsString(binding); |
681 | QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); |
682 | QVariant value = converter ? (*converter)(stringValue) : QVariant(); |
683 | |
684 | QMetaProperty metaProperty = _qobject->metaObject()->property(index: property->coreIndex()); |
685 | if (value.isNull() || metaProperty.userType() != property->propType()) { |
686 | recordError(location: binding->location, description: tr(sourceText: "Cannot assign value %1 to property %2" ).arg(a: stringValue).arg(a: QString::fromUtf8(str: metaProperty.name()))); |
687 | break; |
688 | } |
689 | |
690 | property->writeProperty(target: _qobject, value: value.data(), flags: propertyWriteFlags); |
691 | } |
692 | break; |
693 | } |
694 | } |
695 | |
696 | static QQmlType qmlTypeForObject(QObject *object) |
697 | { |
698 | QQmlType type; |
699 | const QMetaObject *mo = object->metaObject(); |
700 | while (mo && !type.isValid()) { |
701 | type = QQmlMetaType::qmlType(mo); |
702 | mo = mo->superClass(); |
703 | } |
704 | return type; |
705 | } |
706 | |
707 | void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) |
708 | { |
709 | QQmlListProperty<void> savedList; |
710 | qSwap(value1&: _currentList, value2&: savedList); |
711 | |
712 | const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(i: _compiledObjectIndex); |
713 | |
714 | if (_compiledObject->idNameIndex) { |
715 | const QQmlPropertyData *idProperty = propertyData.last(); |
716 | Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id" )); |
717 | if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType() == QMetaType::QString) { |
718 | QV4::CompiledData::Binding idBinding; |
719 | idBinding.propertyNameIndex = 0; // Not used |
720 | idBinding.flags = 0; |
721 | idBinding.type = QV4::CompiledData::Binding::Type_String; |
722 | idBinding.stringIndex = _compiledObject->idNameIndex; |
723 | idBinding.location = _compiledObject->location; // ### |
724 | setPropertyValue(property: idProperty, binding: &idBinding); |
725 | } |
726 | } |
727 | |
728 | // ### this is best done through type-compile-time binding skip lists. |
729 | if (_valueTypeProperty) { |
730 | QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, index: QQmlPropertyIndex(_valueTypeProperty->coreIndex())); |
731 | |
732 | if (binding && !binding->isValueTypeProxy()) { |
733 | QQmlPropertyPrivate::removeBinding(o: _bindingTarget, index: QQmlPropertyIndex(_valueTypeProperty->coreIndex())); |
734 | } else if (binding) { |
735 | QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding *>(binding); |
736 | |
737 | if (qmlTypeForObject(object: _bindingTarget).isValid()) { |
738 | quint32 bindingSkipList = 0; |
739 | |
740 | QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultPropertyOrAlias != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty(); |
741 | |
742 | const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); |
743 | for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { |
744 | QQmlPropertyData *property = binding->propertyNameIndex != 0 ? _propertyCache->property(key: stringAt(idx: binding->propertyNameIndex), object: _qobject, context) : defaultProperty; |
745 | if (property) |
746 | bindingSkipList |= (1 << property->coreIndex()); |
747 | } |
748 | |
749 | proxy->removeBindings(mask: bindingSkipList); |
750 | } |
751 | } |
752 | } |
753 | |
754 | int currentListPropertyIndex = -1; |
755 | |
756 | const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); |
757 | for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { |
758 | QQmlPropertyData *const property = propertyData.at(i); |
759 | if (property) { |
760 | QQmlPropertyData* targetProperty = property; |
761 | if (targetProperty->isAlias()) { |
762 | // follow alias |
763 | auto target = _bindingTarget; |
764 | QQmlPropertyIndex originalIndex(targetProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); |
765 | QQmlPropertyIndex propIndex; |
766 | QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); |
767 | QQmlData *data = QQmlData::get(object: target); |
768 | Q_ASSERT(data && data->propertyCache); |
769 | targetProperty = data->propertyCache->property(index: propIndex.coreIndex()); |
770 | } |
771 | sharedState->requiredProperties.remove(akey: targetProperty); |
772 | } |
773 | |
774 | |
775 | if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) |
776 | continue; |
777 | |
778 | if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) { |
779 | if (!applyDeferredBindings) |
780 | continue; |
781 | } else { |
782 | if (applyDeferredBindings) |
783 | continue; |
784 | } |
785 | |
786 | if (property && property->isQList()) { |
787 | if (property->coreIndex() != currentListPropertyIndex) { |
788 | void *argv[1] = { (void*)&_currentList }; |
789 | QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); |
790 | currentListPropertyIndex = property->coreIndex(); |
791 | } |
792 | } else if (_currentList.object) { |
793 | _currentList = QQmlListProperty<void>(); |
794 | currentListPropertyIndex = -1; |
795 | } |
796 | |
797 | if (!setPropertyBinding(property, binding)) |
798 | return; |
799 | } |
800 | |
801 | qSwap(value1&: _currentList, value2&: savedList); |
802 | } |
803 | |
804 | bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding) |
805 | { |
806 | if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { |
807 | Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); |
808 | QV4::ResolvedTypeReference *tr = resolvedType(id: binding->propertyNameIndex); |
809 | Q_ASSERT(tr); |
810 | QQmlType attachedType = tr->type; |
811 | if (!attachedType.isValid()) { |
812 | QQmlTypeNameCache::Result res = context->imports->query(stringAt(idx: binding->propertyNameIndex)); |
813 | if (res.isValid()) |
814 | attachedType = res.type; |
815 | else |
816 | return false; |
817 | } |
818 | QObject *qmlObject = qmlAttachedPropertiesObject( |
819 | _qobject, func: attachedType.attachedPropertiesFunction(engine: QQmlEnginePrivate::get(e: engine))); |
820 | if (!populateInstance(index: binding->value.objectIndex, instance: qmlObject, bindingTarget: qmlObject, /*value type property*/valueTypeProperty: nullptr)) |
821 | return false; |
822 | return true; |
823 | } |
824 | |
825 | // ### resolve this at compile time |
826 | if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) { |
827 | QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding), |
828 | context->asQQmlContext(), _scopeObject); |
829 | ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; |
830 | ss.d.data()->lineNumber = binding->location.line; |
831 | ss.d.data()->columnNumber = binding->location.column; |
832 | ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; |
833 | ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; |
834 | ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding); |
835 | |
836 | QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | |
837 | QQmlPropertyData::RemoveBindingOnAliasWrite; |
838 | int propertyWriteStatus = -1; |
839 | void *argv[] = { &ss, nullptr, &propertyWriteStatus, &propertyWriteFlags }; |
840 | QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); |
841 | return true; |
842 | } |
843 | |
844 | QObject *createdSubObject = nullptr; |
845 | if (binding->type == QV4::CompiledData::Binding::Type_Object) { |
846 | createdSubObject = createInstance(index: binding->value.objectIndex, parent: _bindingTarget); |
847 | if (!createdSubObject) |
848 | return false; |
849 | } |
850 | |
851 | if (!bindingProperty) // ### error |
852 | return true; |
853 | |
854 | if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { |
855 | const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index: binding->value.objectIndex); |
856 | if (stringAt(idx: obj->inheritedTypeNameIndex).isEmpty()) { |
857 | |
858 | QObject *groupObject = nullptr; |
859 | QQmlGadgetPtrWrapper *valueType = nullptr; |
860 | const QQmlPropertyData *valueTypeProperty = nullptr; |
861 | QObject *bindingTarget = _bindingTarget; |
862 | |
863 | if (QQmlValueTypeFactory::isValueType(idx: bindingProperty->propType())) { |
864 | valueType = QQmlGadgetPtrWrapper::instance(engine, index: bindingProperty->propType()); |
865 | if (!valueType) { |
866 | recordError(location: binding->location, description: tr(sourceText: "Cannot set properties on %1 as it is null" ).arg(a: stringAt(idx: binding->propertyNameIndex))); |
867 | return false; |
868 | } |
869 | |
870 | valueType->read(obj: _qobject, idx: bindingProperty->coreIndex()); |
871 | |
872 | groupObject = valueType; |
873 | valueTypeProperty = bindingProperty; |
874 | } else { |
875 | void *argv[1] = { &groupObject }; |
876 | QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv); |
877 | if (!groupObject) { |
878 | recordError(location: binding->location, description: tr(sourceText: "Cannot set properties on %1 as it is null" ).arg(a: stringAt(idx: binding->propertyNameIndex))); |
879 | return false; |
880 | } |
881 | |
882 | bindingTarget = groupObject; |
883 | } |
884 | |
885 | if (!populateInstance(index: binding->value.objectIndex, instance: groupObject, bindingTarget, valueTypeProperty)) |
886 | return false; |
887 | |
888 | if (valueType) |
889 | valueType->write(obj: _qobject, idx: bindingProperty->coreIndex(), flags: QQmlPropertyData::BypassInterceptor); |
890 | |
891 | return true; |
892 | } |
893 | } |
894 | |
895 | if (_ddata->hasBindingBit(coreIndex: bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) |
896 | && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) |
897 | && !_valueTypeProperty) |
898 | QQmlPropertyPrivate::removeBinding(o: _bindingTarget, index: QQmlPropertyIndex(bindingProperty->coreIndex())); |
899 | |
900 | if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) { |
901 | if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { |
902 | QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; |
903 | int signalIndex = _propertyCache->methodIndexToSignalIndex(index: bindingProperty->coreIndex()); |
904 | QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); |
905 | QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, |
906 | context, _scopeObject, runtimeFunction, currentQmlContext()); |
907 | |
908 | bs->takeExpression(expr); |
909 | } else { |
910 | // When writing bindings to grouped properties implemented as value types, |
911 | // such as point.x: { someExpression; }, then the binding is installed on |
912 | // the point property (_qobjectForBindings) and after evaluating the expression, |
913 | // the result is written to a value type virtual property, that contains the sub-index |
914 | // of the "x" property. |
915 | QQmlBinding::Ptr qmlBinding; |
916 | const QQmlPropertyData *targetProperty = bindingProperty; |
917 | const QQmlPropertyData *subprop = nullptr; |
918 | if (_valueTypeProperty) { |
919 | targetProperty = _valueTypeProperty; |
920 | subprop = bindingProperty; |
921 | } |
922 | if (binding->isTranslationBinding()) { |
923 | qmlBinding = QQmlBinding::createTranslationBinding(unit: compilationUnit, binding, obj: _scopeObject, ctxt: context); |
924 | } else { |
925 | QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; |
926 | qmlBinding = QQmlBinding::create(property: targetProperty, function: runtimeFunction, obj: _scopeObject, ctxt: context, scope: currentQmlContext()); |
927 | } |
928 | |
929 | auto bindingTarget = _bindingTarget; |
930 | auto valueTypeProperty = _valueTypeProperty; |
931 | auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) mutable -> bool { |
932 | if (!qmlBinding->setTarget(bindingTarget, *targetProperty, valueType: subprop) && targetProperty->isAlias()) |
933 | return false; |
934 | |
935 | sharedState->allCreatedBindings.push(o: qmlBinding); |
936 | |
937 | if (bindingProperty->isAlias()) { |
938 | QQmlPropertyPrivate::setBinding(binding: qmlBinding.data(), flags: QQmlPropertyPrivate::DontEnable); |
939 | } else { |
940 | qmlBinding->addToObject(); |
941 | |
942 | if (!valueTypeProperty) { |
943 | QQmlData *targetDeclarativeData = QQmlData::get(object: bindingTarget); |
944 | Q_ASSERT(targetDeclarativeData); |
945 | targetDeclarativeData->setPendingBindingBit(obj: bindingTarget, coreIndex: bindingProperty->coreIndex()); |
946 | } |
947 | } |
948 | |
949 | return true; |
950 | }; |
951 | if (!assignBinding(sharedState.data())) |
952 | pendingAliasBindings.push_back(x: assignBinding); |
953 | } |
954 | return true; |
955 | } |
956 | |
957 | if (binding->type == QV4::CompiledData::Binding::Type_Object) { |
958 | if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { |
959 | // ### determine value source and interceptor casts ahead of time. |
960 | QQmlType type = qmlTypeForObject(object: createdSubObject); |
961 | Q_ASSERT(type.isValid()); |
962 | |
963 | int valueSourceCast = type.propertyValueSourceCast(); |
964 | if (valueSourceCast != -1) { |
965 | QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast); |
966 | QObject *target = createdSubObject->parent(); |
967 | QQmlProperty prop; |
968 | if (_valueTypeProperty) |
969 | prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); |
970 | else |
971 | prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); |
972 | vs->setTarget(prop); |
973 | return true; |
974 | } |
975 | int valueInterceptorCast = type.propertyValueInterceptorCast(); |
976 | if (valueInterceptorCast != -1) { |
977 | QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast); |
978 | QObject *target = createdSubObject->parent(); |
979 | |
980 | QQmlPropertyIndex propertyIndex; |
981 | if (bindingProperty->isAlias()) { |
982 | QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); |
983 | QQmlPropertyIndex propIndex; |
984 | QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); |
985 | QQmlData *data = QQmlData::get(object: target); |
986 | if (!data || !data->propertyCache) { |
987 | qWarning() << "can't resolve property alias for 'on' assignment" ; |
988 | return false; |
989 | } |
990 | |
991 | // we can't have aliasses on subproperties of value types, so: |
992 | QQmlPropertyData targetPropertyData = *data->propertyCache->property(index: propIndex.coreIndex()); |
993 | auto prop = QQmlPropertyPrivate::restore(target, targetPropertyData, nullptr, context); |
994 | vi->setTarget(prop); |
995 | propertyIndex = QQmlPropertyPrivate::propertyIndex(that: prop); |
996 | } else { |
997 | QQmlProperty prop; |
998 | if (_valueTypeProperty) |
999 | prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); |
1000 | else |
1001 | prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); |
1002 | vi->setTarget(prop); |
1003 | propertyIndex = QQmlPropertyPrivate::propertyIndex(that: prop); |
1004 | } |
1005 | |
1006 | QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(obj: target); |
1007 | if (!mo) |
1008 | mo = new QQmlInterceptorMetaObject(target, QQmlData::get(object: target)->propertyCache); |
1009 | mo->registerInterceptor(index: propertyIndex, interceptor: vi); |
1010 | return true; |
1011 | } |
1012 | return false; |
1013 | } |
1014 | |
1015 | // Assigning object to signal property? |
1016 | if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { |
1017 | if (!bindingProperty->isFunction()) { |
1018 | recordError(location: binding->valueLocation, description: tr(sourceText: "Cannot assign an object to signal property %1" ).arg(a: bindingProperty->name(_qobject))); |
1019 | return false; |
1020 | } |
1021 | QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject); |
1022 | if (!method.isValid()) { |
1023 | recordError(location: binding->valueLocation, description: tr(sourceText: "Cannot assign object type %1 with no default method" ).arg(a: QString::fromLatin1(str: createdSubObject->metaObject()->className()))); |
1024 | return false; |
1025 | } |
1026 | |
1027 | QMetaMethod signalMethod = _qobject->metaObject()->method(index: bindingProperty->coreIndex()); |
1028 | if (!QMetaObject::checkConnectArgs(signal: signalMethod, method)) { |
1029 | recordError(location: binding->valueLocation, |
1030 | description: tr(sourceText: "Cannot connect mismatched signal/slot %1 vs %2" ) |
1031 | .arg(a: QString::fromUtf8(str: method.methodSignature())) |
1032 | .arg(a: QString::fromUtf8(str: signalMethod.methodSignature()))); |
1033 | return false; |
1034 | } |
1035 | |
1036 | QQmlPropertyPrivate::connect(sender: _qobject, signal_index: bindingProperty->coreIndex(), receiver: createdSubObject, method_index: method.methodIndex()); |
1037 | return true; |
1038 | } |
1039 | |
1040 | QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | |
1041 | QQmlPropertyData::RemoveBindingOnAliasWrite; |
1042 | int propertyWriteStatus = -1; |
1043 | void *argv[] = { nullptr, nullptr, &propertyWriteStatus, &propertyWriteFlags }; |
1044 | |
1045 | if (const char *iid = QQmlMetaType::interfaceIId(bindingProperty->propType())) { |
1046 | void *ptr = createdSubObject->qt_metacast(iid); |
1047 | if (ptr) { |
1048 | argv[0] = &ptr; |
1049 | QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); |
1050 | } else { |
1051 | recordError(location: binding->location, description: tr(sourceText: "Cannot assign object to interface property" )); |
1052 | return false; |
1053 | } |
1054 | } else if (bindingProperty->propType() == QMetaType::QVariant) { |
1055 | if (bindingProperty->isVarProperty()) { |
1056 | QV4::Scope scope(v4); |
1057 | QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine: engine->handle(), object: createdSubObject)); |
1058 | _vmeMetaObject->setVMEProperty(index: bindingProperty->coreIndex(), v: wrappedObject); |
1059 | } else { |
1060 | QVariant value = QVariant::fromValue(value: createdSubObject); |
1061 | argv[0] = &value; |
1062 | QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); |
1063 | } |
1064 | } else if (bindingProperty->propType() == qMetaTypeId<QJSValue>()) { |
1065 | QV4::Scope scope(v4); |
1066 | QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine: engine->handle(), object: createdSubObject)); |
1067 | if (bindingProperty->isVarProperty()) { |
1068 | _vmeMetaObject->setVMEProperty(index: bindingProperty->coreIndex(), v: wrappedObject); |
1069 | } else { |
1070 | QJSValue value; |
1071 | QJSValuePrivate::setValue(jsval: &value, engine: v4, v: wrappedObject); |
1072 | argv[0] = &value; |
1073 | QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); |
1074 | } |
1075 | } else if (bindingProperty->isQList()) { |
1076 | Q_ASSERT(_currentList.object); |
1077 | |
1078 | void *itemToAdd = createdSubObject; |
1079 | |
1080 | const char *iid = nullptr; |
1081 | int listItemType = QQmlEnginePrivate::get(e: engine)->listType(bindingProperty->propType()); |
1082 | if (listItemType != -1) |
1083 | iid = QQmlMetaType::interfaceIId(listItemType); |
1084 | if (iid) |
1085 | itemToAdd = createdSubObject->qt_metacast(iid); |
1086 | |
1087 | if (_currentList.append) |
1088 | _currentList.append(&_currentList, itemToAdd); |
1089 | else { |
1090 | recordError(location: binding->location, description: tr(sourceText: "Cannot assign object to read only list" )); |
1091 | return false; |
1092 | } |
1093 | |
1094 | } else { |
1095 | // pointer compatibility was tested in QQmlPropertyValidator at type compile time |
1096 | argv[0] = &createdSubObject; |
1097 | QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); |
1098 | } |
1099 | return true; |
1100 | } |
1101 | |
1102 | if (bindingProperty->isQList()) { |
1103 | recordError(location: binding->location, description: tr(sourceText: "Cannot assign primitives to lists" )); |
1104 | return false; |
1105 | } |
1106 | |
1107 | setPropertyValue(property: bindingProperty, binding); |
1108 | return true; |
1109 | } |
1110 | |
1111 | void QQmlObjectCreator::setupFunctions() |
1112 | { |
1113 | QV4::Scope scope(v4); |
1114 | QV4::ScopedValue function(scope); |
1115 | QV4::ScopedContext qmlContext(scope, currentQmlContext()); |
1116 | |
1117 | const quint32_le *functionIdx = _compiledObject->functionOffsetTable(); |
1118 | for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { |
1119 | QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx]; |
1120 | const QString name = runtimeFunction->name()->toQString(); |
1121 | |
1122 | QQmlPropertyData *property = _propertyCache->property(key: name, object: _qobject, context); |
1123 | if (!property->isVMEFunction()) |
1124 | continue; |
1125 | |
1126 | if (runtimeFunction->isGenerator()) |
1127 | function = QV4::GeneratorFunction::create(scope: qmlContext, function: runtimeFunction); |
1128 | else |
1129 | function = QV4::FunctionObject::createScriptFunction(scope: qmlContext, function: runtimeFunction); |
1130 | _vmeMetaObject->setVmeMethod(index: property->coreIndex(), function); |
1131 | } |
1132 | } |
1133 | |
1134 | void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, const QString &description) |
1135 | { |
1136 | QQmlError error; |
1137 | error.setUrl(compilationUnit->url()); |
1138 | error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: location.line)); |
1139 | error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: location.column)); |
1140 | error.setDescription(description); |
1141 | errors << error; |
1142 | } |
1143 | |
1144 | void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const |
1145 | { |
1146 | if (object->id >= 0) |
1147 | context->setIdProperty(object->id, instance); |
1148 | } |
1149 | |
1150 | void QQmlObjectCreator::createQmlContext() |
1151 | { |
1152 | _qmlContext->setM(QV4::QmlContext::create(parent: v4->rootContext(), context, scopeObject: _scopeObject)); |
1153 | } |
1154 | |
1155 | QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) |
1156 | { |
1157 | const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index); |
1158 | QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj); |
1159 | Q_TRACE(QQmlObjectCreator_createInstance_entry, compilationUnit.data(), obj, context->url()); |
1160 | QString typeName; |
1161 | Q_TRACE_EXIT(QQmlObjectCreator_createInstance_exit, typeName); |
1162 | |
1163 | QScopedValueRollback<QQmlObjectCreator*> ocRestore(QQmlEnginePrivate::get(e: engine)->activeObjectCreator, this); |
1164 | |
1165 | bool isComponent = false; |
1166 | QObject *instance = nullptr; |
1167 | QQmlData *ddata = nullptr; |
1168 | QQmlCustomParser *customParser = nullptr; |
1169 | QQmlParserStatus *parserStatus = nullptr; |
1170 | bool installPropertyCache = true; |
1171 | |
1172 | if (obj->flags & QV4::CompiledData::Object::IsComponent) { |
1173 | isComponent = true; |
1174 | QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent); |
1175 | typeName = QStringLiteral("<component>" ); |
1176 | QQmlComponentPrivate::get(c: component)->creationContext = context; |
1177 | instance = component; |
1178 | ddata = QQmlData::get(object: instance, /*create*/true); |
1179 | } else { |
1180 | QV4::ResolvedTypeReference *typeRef = resolvedType(id: obj->inheritedTypeNameIndex); |
1181 | Q_ASSERT(typeRef); |
1182 | installPropertyCache = !typeRef->isFullyDynamicType; |
1183 | QQmlType type = typeRef->type; |
1184 | if (type.isValid() && !type.isInlineComponentType()) { |
1185 | typeName = type.qmlTypeName(); |
1186 | |
1187 | void *ddataMemory = nullptr; |
1188 | type.create(&instance, &ddataMemory, sizeof(QQmlData)); |
1189 | if (!instance) { |
1190 | recordError(location: obj->location, description: tr(sourceText: "Unable to create object of type %1" ).arg(a: stringAt(idx: obj->inheritedTypeNameIndex))); |
1191 | return nullptr; |
1192 | } |
1193 | |
1194 | { |
1195 | QQmlData *ddata = new (ddataMemory) QQmlData; |
1196 | ddata->ownMemory = false; |
1197 | QObjectPrivate* p = QObjectPrivate::get(o: instance); |
1198 | Q_ASSERT(!p->isDeletingChildren); |
1199 | p->declarativeData = ddata; |
1200 | } |
1201 | |
1202 | const int parserStatusCast = type.parserStatusCast(); |
1203 | if (parserStatusCast != -1) |
1204 | parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(instance) + parserStatusCast); |
1205 | |
1206 | customParser = type.customParser(); |
1207 | |
1208 | if (sharedState->rootContext && sharedState->rootContext->isRootObjectInCreation) { |
1209 | QQmlData *ddata = QQmlData::get(object: instance, /*create*/true); |
1210 | ddata->rootObjectInCreation = true; |
1211 | sharedState->rootContext->isRootObjectInCreation = false; |
1212 | } |
1213 | |
1214 | sharedState->allCreatedObjects.push(o: instance); |
1215 | } else { |
1216 | const auto compilationUnit = typeRef->compilationUnit(); |
1217 | Q_ASSERT(compilationUnit); |
1218 | typeName = compilationUnit->fileName(); |
1219 | // compilation unit is shared between root type and its inline component types |
1220 | // so isSingleton errorneously returns true for inline components |
1221 | if (compilationUnit->unitData()->isSingleton() && !type.isInlineComponentType()) |
1222 | { |
1223 | recordError(location: obj->location, description: tr(sourceText: "Composite Singleton Type %1 is not creatable" ).arg(a: stringAt(idx: obj->inheritedTypeNameIndex))); |
1224 | return nullptr; |
1225 | } |
1226 | |
1227 | |
1228 | if (!type.isInlineComponentType()) { |
1229 | QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data()); |
1230 | instance = subCreator.create(); |
1231 | if (!instance) { |
1232 | errors += subCreator.errors; |
1233 | return nullptr; |
1234 | } |
1235 | } else { |
1236 | int subObjectId = type.inlineComponendId(); |
1237 | QScopedValueRollback<int> rollback {compilationUnit->icRoot, subObjectId}; |
1238 | QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data()); |
1239 | instance = subCreator.create(subComponentIndex: subObjectId, parent: nullptr, interrupt: nullptr, flags: CreationFlags::InlineComponent); |
1240 | if (!instance) { |
1241 | errors += subCreator.errors; |
1242 | return nullptr; |
1243 | } |
1244 | } |
1245 | } |
1246 | if (instance->isWidgetType()) { |
1247 | if (parent && parent->isWidgetType()) { |
1248 | QAbstractDeclarativeData::setWidgetParent(instance, parent); |
1249 | } else { |
1250 | // No parent! Layouts need to handle this through a default property that |
1251 | // reparents accordingly. Otherwise the garbage collector will collect. |
1252 | } |
1253 | } else if (parent) { |
1254 | QQml_setParent_noEvent(object: instance, parent); |
1255 | } |
1256 | |
1257 | ddata = QQmlData::get(object: instance, /*create*/true); |
1258 | } |
1259 | |
1260 | Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( |
1261 | compilationUnit.data(), obj, typeName, context->url())); |
1262 | Q_UNUSED(typeName); // only relevant for tracing |
1263 | |
1264 | ddata->lineNumber = obj->location.line; |
1265 | ddata->columnNumber = obj->location.column; |
1266 | |
1267 | ddata->setImplicitDestructible(); |
1268 | // inline components are root objects, but their index is != 0, so we need |
1269 | // an additional check |
1270 | const bool isInlineComponent = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot; |
1271 | if (static_cast<quint32>(index) == /*root object*/0 || ddata->rootObjectInCreation || isInlineComponent) { |
1272 | if (ddata->context) { |
1273 | Q_ASSERT(ddata->context != context); |
1274 | Q_ASSERT(ddata->outerContext); |
1275 | Q_ASSERT(ddata->outerContext != context); |
1276 | QQmlContextData *c = ddata->context; |
1277 | while (c->linkedContext) c = c->linkedContext; |
1278 | c->linkedContext = context; |
1279 | } else { |
1280 | ddata->context = context; |
1281 | } |
1282 | ddata->ownContext = ddata->context; |
1283 | } else if (!ddata->context) { |
1284 | ddata->context = context; |
1285 | } |
1286 | |
1287 | context->addObject(data: ddata); |
1288 | |
1289 | if (parserStatus) { |
1290 | parserStatus->classBegin(); |
1291 | // push() the profiler state here, together with the parserStatus, as we'll pop() them |
1292 | // together, too. |
1293 | Q_QML_OC_PROFILE(sharedState->profiler, sharedState->profiler.push(obj)); |
1294 | sharedState->allParserStatusCallbacks.push(o: parserStatus); |
1295 | parserStatus->d = &sharedState->allParserStatusCallbacks.top(); |
1296 | } |
1297 | |
1298 | // Register the context object in the context early on in order for pending binding |
1299 | // initialization to find it available. |
1300 | if (isContextObject) |
1301 | context->contextObject = instance; |
1302 | |
1303 | if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { |
1304 | customParser->engine = QQmlEnginePrivate::get(e: engine); |
1305 | customParser->imports = compilationUnit->typeNameCache.data(); |
1306 | |
1307 | QList<const QV4::CompiledData::Binding *> bindings; |
1308 | const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index); |
1309 | const QV4::CompiledData::Binding *binding = obj->bindingTable(); |
1310 | for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { |
1311 | if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) { |
1312 | bindings << binding; |
1313 | } |
1314 | } |
1315 | customParser->applyBindings(instance, compilationUnit.data(), bindings); |
1316 | |
1317 | customParser->engine = nullptr; |
1318 | customParser->imports = (QQmlTypeNameCache*)nullptr; |
1319 | } |
1320 | |
1321 | if (isComponent) { |
1322 | registerObjectWithContextById(object: obj, instance); |
1323 | return instance; |
1324 | } |
1325 | |
1326 | QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches->at(index); |
1327 | Q_ASSERT(!cache.isNull()); |
1328 | if (installPropertyCache) { |
1329 | if (ddata->propertyCache) |
1330 | ddata->propertyCache->release();; |
1331 | ddata->propertyCache = cache.data(); |
1332 | ddata->propertyCache->addref(); |
1333 | } |
1334 | |
1335 | QObject *scopeObject = instance; |
1336 | qSwap(value1&: _scopeObject, value2&: scopeObject); |
1337 | |
1338 | Q_ASSERT(sharedState->allJavaScriptObjects); |
1339 | *sharedState->allJavaScriptObjects = QV4::QObjectWrapper::wrap(engine: v4, object: instance); |
1340 | ++sharedState->allJavaScriptObjects; |
1341 | |
1342 | QV4::Scope valueScope(v4); |
1343 | QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); |
1344 | |
1345 | qSwap(value1&: _qmlContext, value2&: qmlContext); |
1346 | |
1347 | bool ok = populateInstance(index, instance, /*binding target*/bindingTarget: instance, /*value type property*/valueTypeProperty: nullptr); |
1348 | if (ok) { |
1349 | if (isContextObject && !pendingAliasBindings.empty()) { |
1350 | bool processedAtLeastOneBinding = false; |
1351 | do { |
1352 | processedAtLeastOneBinding = false; |
1353 | for (std::vector<PendingAliasBinding>::iterator it = pendingAliasBindings.begin(); |
1354 | it != pendingAliasBindings.end(); ) { |
1355 | if ((*it)(sharedState.data())) { |
1356 | it = pendingAliasBindings.erase(position: it); |
1357 | processedAtLeastOneBinding = true; |
1358 | } else { |
1359 | ++it; |
1360 | } |
1361 | } |
1362 | } while (processedAtLeastOneBinding && pendingAliasBindings.empty()); |
1363 | Q_ASSERT(pendingAliasBindings.empty()); |
1364 | } |
1365 | } else { |
1366 | // an error occurred, so we can't setup the pending alias bindings |
1367 | pendingAliasBindings.clear(); |
1368 | } |
1369 | |
1370 | qSwap(value1&: _qmlContext, value2&: qmlContext); |
1371 | qSwap(value1&: _scopeObject, value2&: scopeObject); |
1372 | |
1373 | return ok ? instance : nullptr; |
1374 | } |
1375 | |
1376 | QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) |
1377 | { |
1378 | Q_ASSERT(phase == ObjectsCreated || phase == Finalizing); |
1379 | phase = Finalizing; |
1380 | |
1381 | QQmlObjectCreatorRecursionWatcher watcher(this); |
1382 | QScopedValueRollback<QQmlObjectCreator*> ocRestore(QQmlEnginePrivate::get(e: engine)->activeObjectCreator, this); |
1383 | |
1384 | while (!sharedState->allCreatedBindings.isEmpty()) { |
1385 | QQmlAbstractBinding::Ptr b = sharedState->allCreatedBindings.pop(); |
1386 | Q_ASSERT(b); |
1387 | // skip, if b is not added to an object |
1388 | if (!b->isAddedToObject()) |
1389 | continue; |
1390 | QQmlData *data = QQmlData::get(object: b->targetObject()); |
1391 | Q_ASSERT(data); |
1392 | data->clearPendingBindingBit(coreIndex: b->targetPropertyIndex().coreIndex()); |
1393 | b->setEnabled(e: true, f: QQmlPropertyData::BypassInterceptor | |
1394 | QQmlPropertyData::DontRemoveBinding); |
1395 | if (!b->isValueTypeProxy()) { |
1396 | QQmlBinding *binding = static_cast<QQmlBinding*>(b.data()); |
1397 | if (!binding->hasError() && !binding->hasDependencies() |
1398 | && binding->context() && !binding->context()->unresolvedNames) |
1399 | b->removeFromObject(); |
1400 | } |
1401 | |
1402 | if (watcher.hasRecursed() || interrupt.shouldInterrupt()) |
1403 | return nullptr; |
1404 | } |
1405 | |
1406 | if (QQmlVME::componentCompleteEnabled()) { // the qml designer does the component complete later |
1407 | while (!sharedState->allParserStatusCallbacks.isEmpty()) { |
1408 | QQmlObjectCompletionProfiler profiler(&sharedState->profiler); |
1409 | QQmlParserStatus *status = sharedState->allParserStatusCallbacks.pop(); |
1410 | |
1411 | if (status && status->d) { |
1412 | status->d = nullptr; |
1413 | status->componentComplete(); |
1414 | } |
1415 | |
1416 | if (watcher.hasRecursed() || interrupt.shouldInterrupt()) |
1417 | return nullptr; |
1418 | } |
1419 | } |
1420 | |
1421 | for (int ii = 0; ii < sharedState->finalizeCallbacks.count(); ++ii) { |
1422 | QQmlEnginePrivate::FinalizeCallback callback = sharedState->finalizeCallbacks.at(i: ii); |
1423 | QObject *obj = callback.first; |
1424 | if (obj) { |
1425 | void *args[] = { nullptr }; |
1426 | QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); |
1427 | } |
1428 | if (watcher.hasRecursed()) |
1429 | return nullptr; |
1430 | } |
1431 | sharedState->finalizeCallbacks.clear(); |
1432 | |
1433 | while (sharedState->componentAttached) { |
1434 | QQmlComponentAttached *a = sharedState->componentAttached; |
1435 | a->rem(); |
1436 | QQmlData *d = QQmlData::get(object: a->parent()); |
1437 | Q_ASSERT(d); |
1438 | Q_ASSERT(d->context); |
1439 | a->add(a: &d->context->componentAttached); |
1440 | if (QQmlVME::componentCompleteEnabled()) |
1441 | emit a->completed(); |
1442 | |
1443 | if (watcher.hasRecursed() || interrupt.shouldInterrupt()) |
1444 | return nullptr; |
1445 | } |
1446 | |
1447 | phase = Done; |
1448 | |
1449 | return sharedState->rootContext; |
1450 | } |
1451 | |
1452 | void QQmlObjectCreator::clear() |
1453 | { |
1454 | if (phase == Done || phase == Finalizing || phase == Startup) |
1455 | return; |
1456 | Q_ASSERT(phase != Startup); |
1457 | |
1458 | while (!sharedState->allCreatedObjects.isEmpty()) { |
1459 | auto object = sharedState->allCreatedObjects.pop(); |
1460 | if (engine->objectOwnership(object) != QQmlEngine::CppOwnership) { |
1461 | delete object; |
1462 | } |
1463 | } |
1464 | |
1465 | while (sharedState->componentAttached) { |
1466 | QQmlComponentAttached *a = sharedState->componentAttached; |
1467 | a->rem(); |
1468 | } |
1469 | |
1470 | phase = Done; |
1471 | } |
1472 | |
1473 | bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty) |
1474 | { |
1475 | QQmlData *declarativeData = QQmlData::get(object: instance, /*create*/true); |
1476 | |
1477 | qSwap(value1&: _qobject, value2&: instance); |
1478 | qSwap(value1&: _valueTypeProperty, value2&: valueTypeProperty); |
1479 | qSwap(value1&: _compiledObjectIndex, value2&: index); |
1480 | const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index: _compiledObjectIndex); |
1481 | qSwap(value1&: _compiledObject, value2&: obj); |
1482 | qSwap(value1&: _ddata, value2&: declarativeData); |
1483 | qSwap(value1&: _bindingTarget, value2&: bindingTarget); |
1484 | |
1485 | QV4::Scope valueScope(v4); |
1486 | QV4::ScopedValue scopeObjectProtector(valueScope); |
1487 | |
1488 | QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches->at(index: _compiledObjectIndex); |
1489 | |
1490 | QQmlVMEMetaObject *vmeMetaObject = nullptr; |
1491 | if (propertyCaches->needsVMEMetaObject(index: _compiledObjectIndex)) { |
1492 | Q_ASSERT(!cache.isNull()); |
1493 | // install on _object |
1494 | vmeMetaObject = new QQmlVMEMetaObject(v4, _qobject, cache, compilationUnit, _compiledObjectIndex); |
1495 | if (_ddata->propertyCache) |
1496 | _ddata->propertyCache->release(); |
1497 | _ddata->propertyCache = cache.data(); |
1498 | _ddata->propertyCache->addref(); |
1499 | scopeObjectProtector = _ddata->jsWrapper.value(); |
1500 | } else { |
1501 | vmeMetaObject = QQmlVMEMetaObject::get(obj: _qobject); |
1502 | } |
1503 | |
1504 | registerObjectWithContextById(object: _compiledObject, instance: _qobject); |
1505 | |
1506 | qSwap(value1&: _propertyCache, value2&: cache); |
1507 | qSwap(value1&: _vmeMetaObject, value2&: vmeMetaObject); |
1508 | |
1509 | if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) |
1510 | _ddata->deferData(objectIndex: _compiledObjectIndex, compilationUnit, context); |
1511 | |
1512 | QSet<QString> postHocRequired; |
1513 | for (auto it = _compiledObject->requiredPropertyExtraDataBegin(); it != _compiledObject->requiredPropertyExtraDataEnd(); ++it) |
1514 | postHocRequired.insert(value: stringAt(idx: it->nameIndex)); |
1515 | bool hadInheritedRequiredProperties = !postHocRequired.empty(); |
1516 | |
1517 | for (int propertyIndex = 0; propertyIndex != _compiledObject->propertyCount(); ++propertyIndex) { |
1518 | const QV4::CompiledData::Property* property = _compiledObject->propertiesBegin() + propertyIndex; |
1519 | QQmlPropertyData *propertyData = _propertyCache->property(index: _propertyCache->propertyOffset() + propertyIndex); |
1520 | // only compute stringAt if there's a chance for the lookup to succeed |
1521 | auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(value: stringAt(idx: property->nameIndex)); |
1522 | if (!property->isRequired && postHocRequired.end() == postHocIt) |
1523 | continue; |
1524 | if (postHocIt != postHocRequired.end()) |
1525 | postHocRequired.erase(i: postHocIt); |
1526 | sharedState->hadRequiredProperties = true; |
1527 | sharedState->requiredProperties.insert(akey: propertyData, |
1528 | avalue: RequiredPropertyInfo {.propertyName: compilationUnit->stringAt(index: property->nameIndex), .fileUrl: compilationUnit->finalUrl(), .location: property->location, .aliasesToRequired: {}}); |
1529 | |
1530 | } |
1531 | |
1532 | for (int i = 0; i <= _propertyCache->propertyOffset(); ++i) { |
1533 | QQmlPropertyData *propertyData = _propertyCache->maybeUnresolvedProperty(i); |
1534 | if (!propertyData) |
1535 | continue; |
1536 | if (!propertyData->isRequired() && postHocRequired.isEmpty()) |
1537 | continue; |
1538 | QString name = propertyData->name(_qobject); |
1539 | auto postHocIt = postHocRequired.find(value: name); |
1540 | if (!propertyData->isRequired() && postHocRequired.end() == postHocIt ) |
1541 | continue; |
1542 | |
1543 | if (postHocIt != postHocRequired.end()) |
1544 | postHocRequired.erase(i: postHocIt); |
1545 | |
1546 | sharedState->hadRequiredProperties = true; |
1547 | sharedState->requiredProperties.insert(akey: propertyData, avalue: RequiredPropertyInfo {.propertyName: name, .fileUrl: compilationUnit->finalUrl(), .location: _compiledObject->location, .aliasesToRequired: {}}); |
1548 | } |
1549 | if (!postHocRequired.isEmpty() && hadInheritedRequiredProperties) |
1550 | recordError(location: {}, description: QLatin1String("Property %1 was marked as required but does not exist" ).arg(args: *postHocRequired.begin())); |
1551 | |
1552 | if (_compiledObject->nFunctions > 0) |
1553 | setupFunctions(); |
1554 | setupBindings(); |
1555 | |
1556 | for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) { |
1557 | const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex; |
1558 | const auto originalAlias = alias; |
1559 | while (alias->aliasToLocalAlias) |
1560 | alias = _compiledObject->aliasesBegin() + alias->localAliasIndex; |
1561 | Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); |
1562 | if (!context->idValues->wasSet()) |
1563 | continue; |
1564 | QObject *target = context->idValues[alias->targetObjectId].data(); |
1565 | if (!target) |
1566 | continue; |
1567 | QQmlData *targetDData = QQmlData::get(object: target, /*create*/false); |
1568 | if (targetDData == nullptr || targetDData->propertyCache == nullptr) |
1569 | continue; |
1570 | int coreIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias->encodedMetaPropertyIndex).coreIndex(); |
1571 | QQmlPropertyData *const targetProperty = targetDData->propertyCache->property(index: coreIndex); |
1572 | if (!targetProperty) |
1573 | continue; |
1574 | auto it = sharedState->requiredProperties.find(akey: targetProperty); |
1575 | if (it != sharedState->requiredProperties.end()) |
1576 | it->aliasesToRequired.push_back(t: AliasToRequiredInfo {.propertyName: compilationUnit->stringAt(index: originalAlias->nameIndex), .fileUrl: compilationUnit->finalUrl()}); |
1577 | } |
1578 | |
1579 | qSwap(value1&: _vmeMetaObject, value2&: vmeMetaObject); |
1580 | qSwap(value1&: _bindingTarget, value2&: bindingTarget); |
1581 | qSwap(value1&: _ddata, value2&: declarativeData); |
1582 | qSwap(value1&: _compiledObject, value2&: obj); |
1583 | qSwap(value1&: _compiledObjectIndex, value2&: index); |
1584 | qSwap(value1&: _valueTypeProperty, value2&: valueTypeProperty); |
1585 | qSwap(value1&: _qobject, value2&: instance); |
1586 | qSwap(value1&: _propertyCache, value2&: cache); |
1587 | |
1588 | return errors.isEmpty(); |
1589 | } |
1590 | |
1591 | |
1592 | |
1593 | |
1594 | QQmlObjectCreatorRecursionWatcher::QQmlObjectCreatorRecursionWatcher(QQmlObjectCreator *creator) |
1595 | : sharedState(creator->sharedState) |
1596 | , watcher(creator->sharedState.data()) |
1597 | { |
1598 | } |
1599 | |