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 | #ifndef QQMLPROPERTYCACHECREATOR_P_H |
40 | #define QQMLPROPERTYCACHECREATOR_P_H |
41 | |
42 | // |
43 | // W A R N I N G |
44 | // ------------- |
45 | // |
46 | // This file is not part of the Qt API. It exists purely as an |
47 | // implementation detail. This header file may change from version to |
48 | // version without notice, or even be removed. |
49 | // |
50 | // We mean it. |
51 | // |
52 | |
53 | #include <private/qqmlvaluetype_p.h> |
54 | #include <private/qqmlengine_p.h> |
55 | #include <private/qqmlmetaobject_p.h> |
56 | #include <private/qqmlpropertyresolver_p.h> |
57 | #include <private/qqmltypedata_p.h> |
58 | #include <private/inlinecomponentutils_p.h> |
59 | #include <private/qqmlsourcecoordinate_p.h> |
60 | |
61 | #include <QScopedValueRollback> |
62 | #include <vector> |
63 | |
64 | QT_BEGIN_NAMESPACE |
65 | |
66 | inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location, |
67 | const QString &description) |
68 | { |
69 | QQmlError error; |
70 | error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: location.line)); |
71 | error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: location.column)); |
72 | error.setDescription(description); |
73 | return error; |
74 | } |
75 | |
76 | struct QQmlBindingInstantiationContext { |
77 | QQmlBindingInstantiationContext() {} |
78 | QQmlBindingInstantiationContext(int referencingObjectIndex, |
79 | const QV4::CompiledData::Binding *instantiatingBinding, |
80 | const QString &instantiatingPropertyName, |
81 | QQmlPropertyCache *referencingObjectPropertyCache); |
82 | |
83 | bool resolveInstantiatingProperty(); |
84 | QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; |
85 | |
86 | int referencingObjectIndex = -1; |
87 | const QV4::CompiledData::Binding *instantiatingBinding = nullptr; |
88 | QString instantiatingPropertyName; |
89 | QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache; |
90 | QQmlPropertyData *instantiatingProperty = nullptr; |
91 | }; |
92 | |
93 | struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext> |
94 | { |
95 | void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; |
96 | }; |
97 | |
98 | struct QQmlPropertyCacheCreatorBase |
99 | { |
100 | Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) |
101 | public: |
102 | static QAtomicInt classIndexCounter; |
103 | |
104 | static int metaTypeForPropertyType(QV4::CompiledData::BuiltinType type); |
105 | |
106 | static QByteArray createClassNameTypeByUrl(const QUrl &url); |
107 | |
108 | static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, int icId); |
109 | }; |
110 | |
111 | template <typename ObjectContainer> |
112 | class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase |
113 | { |
114 | public: |
115 | typedef typename ObjectContainer::CompiledObject CompiledObject; |
116 | |
117 | QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, |
118 | QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, |
119 | QQmlEnginePrivate *enginePrivate, |
120 | const ObjectContainer *objectContainer, const QQmlImports *imports, |
121 | const QByteArray &typeClassName); |
122 | |
123 | QQmlError buildMetaObjects(); |
124 | |
125 | enum class VMEMetaObjectIsRequired { |
126 | Maybe, |
127 | Always |
128 | }; |
129 | protected: |
130 | QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired); |
131 | QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const; |
132 | QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); |
133 | |
134 | int metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, QString *customTypeName = nullptr); |
135 | |
136 | QString stringAt(int index) const { return objectContainer->stringAt(index); } |
137 | |
138 | QQmlEnginePrivate * const enginePrivate; |
139 | const ObjectContainer * const objectContainer; |
140 | const QQmlImports * const imports; |
141 | QQmlPropertyCacheVector *propertyCaches; |
142 | QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings; |
143 | QByteArray typeClassName; // not const as we temporarily chang it for inline components |
144 | unsigned int currentRoot; // set to objectID of inline component root when handling inline components |
145 | }; |
146 | |
147 | template <typename ObjectContainer> |
148 | inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, |
149 | QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, |
150 | QQmlEnginePrivate *enginePrivate, |
151 | const ObjectContainer *objectContainer, const QQmlImports *imports, |
152 | const QByteArray &typeClassName) |
153 | : enginePrivate(enginePrivate) |
154 | , objectContainer(objectContainer) |
155 | , imports(imports) |
156 | , propertyCaches(propertyCaches) |
157 | , pendingGroupPropertyBindings(pendingGroupPropertyBindings) |
158 | , typeClassName(typeClassName) |
159 | , currentRoot(-1) |
160 | { |
161 | propertyCaches->resize(size: objectContainer->objectCount()); |
162 | } |
163 | |
164 | template <typename ObjectContainer> |
165 | inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() |
166 | { |
167 | using namespace icutils; |
168 | QQmlBindingInstantiationContext context; |
169 | |
170 | // get a list of all inline components |
171 | using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type; |
172 | std::vector<InlineComponent> allICs {}; |
173 | for (int i=0; i != objectContainer->objectCount(); ++i) { |
174 | const CompiledObject *obj = objectContainer->objectAt(i); |
175 | for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { |
176 | allICs.push_back(*it); |
177 | } |
178 | } |
179 | |
180 | // create a graph on inline components referencing inline components |
181 | std::vector<Node> nodes; |
182 | nodes.resize(allICs.size()); |
183 | std::iota(first: nodes.begin(), last: nodes.end(), value: 0); |
184 | AdjacencyList adjacencyList; |
185 | adjacencyList.resize(new_size: nodes.size()); |
186 | fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs); |
187 | |
188 | bool hasCycle = false; |
189 | auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle); |
190 | |
191 | if (hasCycle) { |
192 | QQmlError diag; |
193 | diag.setDescription(QLatin1String("Inline components form a cycle!" )); |
194 | return diag; |
195 | } |
196 | |
197 | // create meta objects for inline components before compiling actual root component |
198 | for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { |
199 | const auto &ic = allICs[nodeIt->index]; |
200 | QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex); |
201 | Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr); |
202 | Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet |
203 | |
204 | QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() }; |
205 | QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName}; |
206 | QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex}; |
207 | QQmlError diag = buildMetaObjectRecursively(objectIndex: ic.objectIndex, context, isVMERequired: VMEMetaObjectIsRequired::Always); |
208 | if (diag.isValid()) { |
209 | return diag; |
210 | } |
211 | typeRef->typePropertyCache = propertyCaches->at(index: ic.objectIndex); |
212 | Q_ASSERT(!typeRef->typePropertyCache.isNull()); |
213 | } |
214 | |
215 | return buildMetaObjectRecursively(/*root object*/objectIndex: 0, context, isVMERequired: VMEMetaObjectIsRequired::Maybe); |
216 | } |
217 | |
218 | template <typename ObjectContainer> |
219 | inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired) |
220 | { |
221 | auto isAddressable = [](const QUrl &url) { |
222 | const QString fileName = url.fileName(); |
223 | return !fileName.isEmpty() && fileName.front().isUpper(); |
224 | }; |
225 | |
226 | const CompiledObject *obj = objectContainer->objectAt(objectIndex); |
227 | bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0 |
228 | || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0 |
229 | || (((obj->flags & QV4::CompiledData::Object::IsComponent) |
230 | || (objectIndex == 0 && isAddressable(objectContainer->url()))) |
231 | && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType); |
232 | |
233 | if (!needVMEMetaObject) { |
234 | auto binding = obj->bindingsBegin(); |
235 | auto end = obj->bindingsEnd(); |
236 | for ( ; binding != end; ++binding) { |
237 | if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { |
238 | // If the on assignment is inside a group property, we need to distinguish between QObject based |
239 | // group properties and value type group properties. For the former the base type is derived from |
240 | // the property that references us, for the latter we only need a meta-object on the referencing object |
241 | // because interceptors can't go to the shared value type instances. |
242 | if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(idx: context.instantiatingProperty->propType())) { |
243 | if (!propertyCaches->needsVMEMetaObject(index: context.referencingObjectIndex)) { |
244 | const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); |
245 | auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); |
246 | Q_ASSERT(typeRef); |
247 | QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(p: enginePrivate)); |
248 | QQmlError error = createMetaObject(objectIndex: context.referencingObjectIndex, obj, baseTypeCache); |
249 | if (error.isValid()) |
250 | return error; |
251 | } |
252 | } else { |
253 | // On assignments are implemented using value interceptors, which require a VME meta object. |
254 | needVMEMetaObject = true; |
255 | } |
256 | break; |
257 | } |
258 | } |
259 | } |
260 | |
261 | QQmlRefPointer<QQmlPropertyCache> baseTypeCache; |
262 | { |
263 | QQmlError error; |
264 | baseTypeCache = propertyCacheForObject(obj, context, error: &error); |
265 | if (error.isValid()) |
266 | return error; |
267 | } |
268 | |
269 | if (baseTypeCache) { |
270 | if (needVMEMetaObject) { |
271 | QQmlError error = createMetaObject(objectIndex, obj, baseTypeCache); |
272 | if (error.isValid()) |
273 | return error; |
274 | } else { |
275 | propertyCaches->set(index: objectIndex, replacement: baseTypeCache); |
276 | } |
277 | } |
278 | |
279 | if (QQmlPropertyCache *thisCache = propertyCaches->at(index: objectIndex)) { |
280 | auto binding = obj->bindingsBegin(); |
281 | auto end = obj->bindingsEnd(); |
282 | for ( ; binding != end; ++binding) |
283 | if (binding->type >= QV4::CompiledData::Binding::Type_Object) { |
284 | QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(index: binding->propertyNameIndex), thisCache); |
285 | |
286 | // Binding to group property where we failed to look up the type of the |
287 | // property? Possibly a group property that is an alias that's not resolved yet. |
288 | // Let's attempt to resolve it after we're done with the aliases and fill in the |
289 | // propertyCaches entry then. |
290 | if (!context.resolveInstantiatingProperty()) |
291 | pendingGroupPropertyBindings->append(t: context); |
292 | |
293 | QQmlError error = buildMetaObjectRecursively(objectIndex: binding->value.objectIndex, context, isVMERequired: VMEMetaObjectIsRequired::Maybe); |
294 | if (error.isValid()) |
295 | return error; |
296 | } |
297 | } |
298 | |
299 | QQmlError noError; |
300 | return noError; |
301 | } |
302 | |
303 | template <typename ObjectContainer> |
304 | inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const |
305 | { |
306 | if (context.instantiatingProperty) { |
307 | return context.instantiatingPropertyCache(enginePrivate); |
308 | } else if (obj->inheritedTypeNameIndex != 0) { |
309 | auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); |
310 | QQmlType qmltype = typeRef->type; |
311 | Q_ASSERT(typeRef); |
312 | |
313 | if (typeRef->isFullyDynamicType) { |
314 | if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { |
315 | *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Fully dynamic types cannot declare new properties." )); |
316 | return nullptr; |
317 | } |
318 | if (obj->signalCount() > 0) { |
319 | *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Fully dynamic types cannot declare new signals." )); |
320 | return nullptr; |
321 | } |
322 | if (obj->functionCount() > 0) { |
323 | *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Fully Dynamic types cannot declare new functions." )); |
324 | return nullptr; |
325 | } |
326 | } |
327 | |
328 | return typeRef->createPropertyCache(QQmlEnginePrivate::get(p: enginePrivate)); |
329 | } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { |
330 | auto *typeRef = objectContainer->resolvedType( |
331 | context.instantiatingBinding->propertyNameIndex); |
332 | Q_ASSERT(typeRef); |
333 | QQmlType qmltype = typeRef->type; |
334 | if (!qmltype.isValid()) { |
335 | imports->resolveType(stringAt(index: context.instantiatingBinding->propertyNameIndex), |
336 | &qmltype, nullptr, nullptr, nullptr); |
337 | } |
338 | |
339 | const QMetaObject *attachedMo = qmltype.attachedPropertiesType(engine: enginePrivate); |
340 | if (!attachedMo) { |
341 | *error = qQmlCompileError(location: context.instantiatingBinding->location, description: QQmlPropertyCacheCreatorBase::tr(sourceText: "Non-existent attached object" )); |
342 | return nullptr; |
343 | } |
344 | return enginePrivate->cache(metaObject: attachedMo); |
345 | } |
346 | return nullptr; |
347 | } |
348 | |
349 | template <typename ObjectContainer> |
350 | inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) |
351 | { |
352 | QQmlRefPointer<QQmlPropertyCache> cache; |
353 | cache.adopt(baseTypeCache->copyAndReserve(propertyCount: obj->propertyCount() + obj->aliasCount(), |
354 | methodCount: obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), |
355 | signalCount: obj->signalCount() + obj->propertyCount() + obj->aliasCount(), enumCount: obj->enumCount())); |
356 | |
357 | propertyCaches->set(index: objectIndex, replacement: cache); |
358 | propertyCaches->setNeedsVMEMetaObject(objectIndex); |
359 | |
360 | QByteArray newClassName; |
361 | |
362 | if (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) { |
363 | newClassName = typeClassName; |
364 | } |
365 | if (newClassName.isEmpty()) { |
366 | newClassName = QQmlMetaObject(baseTypeCache.data()).className(); |
367 | newClassName.append(s: "_QML_" ); |
368 | newClassName.append(a: QByteArray::number(classIndexCounter.fetchAndAddRelaxed(valueToAdd: 1))); |
369 | } |
370 | |
371 | cache->_dynamicClassName = newClassName; |
372 | |
373 | int varPropCount = 0; |
374 | |
375 | QQmlPropertyResolver resolver(baseTypeCache); |
376 | |
377 | auto p = obj->propertiesBegin(); |
378 | auto pend = obj->propertiesEnd(); |
379 | for ( ; p != pend; ++p) { |
380 | if (p->builtinType() == QV4::CompiledData::BuiltinType::Var) |
381 | varPropCount++; |
382 | |
383 | bool notInRevision = false; |
384 | QQmlPropertyData *d = resolver.property(stringAt(index: p->nameIndex), ¬InRevision); |
385 | if (d && d->isFinal()) |
386 | return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Cannot override FINAL property" )); |
387 | } |
388 | |
389 | auto a = obj->aliasesBegin(); |
390 | auto aend = obj->aliasesEnd(); |
391 | for ( ; a != aend; ++a) { |
392 | bool notInRevision = false; |
393 | QQmlPropertyData *d = resolver.property(stringAt(index: a->nameIndex), ¬InRevision); |
394 | if (d && d->isFinal()) |
395 | return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Cannot override FINAL property" )); |
396 | } |
397 | |
398 | int effectivePropertyIndex = cache->propertyIndexCacheStart; |
399 | int effectiveMethodIndex = cache->methodIndexCacheStart; |
400 | |
401 | // For property change signal override detection. |
402 | // We prepopulate a set of signal names which already exist in the object, |
403 | // and throw an error if there is a signal/method defined as an override. |
404 | QSet<QString> seenSignals; |
405 | seenSignals << QStringLiteral("destroyed" ) << QStringLiteral("parentChanged" ) << QStringLiteral("objectNameChanged" ); |
406 | QQmlPropertyCache *parentCache = cache.data(); |
407 | while ((parentCache = parentCache->parent())) { |
408 | if (int pSigCount = parentCache->signalCount()) { |
409 | int pSigOffset = parentCache->signalOffset(); |
410 | for (int i = pSigOffset; i < pSigCount; ++i) { |
411 | QQmlPropertyData *currPSig = parentCache->signal(index: i); |
412 | // XXX TODO: find a better way to get signal name from the property data :-/ |
413 | for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); |
414 | iter != parentCache->stringCache.end(); ++iter) { |
415 | if (currPSig == (*iter).second) { |
416 | seenSignals.insert(value: iter.key()); |
417 | break; |
418 | } |
419 | } |
420 | } |
421 | } |
422 | } |
423 | |
424 | // Set up notify signals for properties - first normal, then alias |
425 | p = obj->propertiesBegin(); |
426 | pend = obj->propertiesEnd(); |
427 | for ( ; p != pend; ++p) { |
428 | auto flags = QQmlPropertyData::defaultSignalFlags(); |
429 | |
430 | QString changedSigName = stringAt(index: p->nameIndex) + QLatin1String("Changed" ); |
431 | seenSignals.insert(value: changedSigName); |
432 | |
433 | cache->appendSignal(changedSigName, flags, coreIndex: effectiveMethodIndex++); |
434 | } |
435 | |
436 | a = obj->aliasesBegin(); |
437 | aend = obj->aliasesEnd(); |
438 | for ( ; a != aend; ++a) { |
439 | auto flags = QQmlPropertyData::defaultSignalFlags(); |
440 | |
441 | QString changedSigName = stringAt(index: a->nameIndex) + QLatin1String("Changed" ); |
442 | seenSignals.insert(value: changedSigName); |
443 | |
444 | cache->appendSignal(changedSigName, flags, coreIndex: effectiveMethodIndex++); |
445 | } |
446 | |
447 | auto e = obj->enumsBegin(); |
448 | auto eend = obj->enumsEnd(); |
449 | for ( ; e != eend; ++e) { |
450 | const int enumValueCount = e->enumValueCount(); |
451 | QVector<QQmlEnumValue> values; |
452 | values.reserve(size: enumValueCount); |
453 | |
454 | auto enumValue = e->enumValuesBegin(); |
455 | auto end = e->enumValuesEnd(); |
456 | for ( ; enumValue != end; ++enumValue) |
457 | values.append(t: QQmlEnumValue(stringAt(index: enumValue->nameIndex), enumValue->value)); |
458 | |
459 | cache->appendEnum(stringAt(index: e->nameIndex), values); |
460 | } |
461 | |
462 | // Dynamic signals |
463 | auto s = obj->signalsBegin(); |
464 | auto send = obj->signalsEnd(); |
465 | for ( ; s != send; ++s) { |
466 | const int paramCount = s->parameterCount(); |
467 | |
468 | QList<QByteArray> names; |
469 | names.reserve(size: paramCount); |
470 | QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); |
471 | |
472 | if (paramCount) { |
473 | paramTypes[0] = paramCount; |
474 | |
475 | int i = 0; |
476 | auto param = s->parametersBegin(); |
477 | auto end = s->parametersEnd(); |
478 | for ( ; param != end; ++param, ++i) { |
479 | names.append(stringAt(index: param->nameIndex).toUtf8()); |
480 | |
481 | QString customTypeName; |
482 | auto type = metaTypeForParameter(param: param->type, customTypeName: &customTypeName); |
483 | if (type == QMetaType::UnknownType) |
484 | return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid signal parameter type: %1" ).arg(a: customTypeName)); |
485 | |
486 | paramTypes[i + 1] = type; |
487 | } |
488 | } |
489 | |
490 | auto flags = QQmlPropertyData::defaultSignalFlags(); |
491 | if (paramCount) |
492 | flags.setHasArguments(true); |
493 | |
494 | QString signalName = stringAt(index: s->nameIndex); |
495 | if (seenSignals.contains(value: signalName)) |
496 | return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Duplicate signal name: invalid override of property change signal or superclass signal" )); |
497 | seenSignals.insert(value: signalName); |
498 | |
499 | cache->appendSignal(signalName, flags, coreIndex: effectiveMethodIndex++, |
500 | types: paramCount?paramTypes.constData():nullptr, names); |
501 | } |
502 | |
503 | |
504 | // Dynamic slots |
505 | auto function = objectContainer->objectFunctionsBegin(obj); |
506 | auto fend = objectContainer->objectFunctionsEnd(obj); |
507 | for ( ; function != fend; ++function) { |
508 | auto flags = QQmlPropertyData::defaultSlotFlags(); |
509 | |
510 | const QString slotName = stringAt(index: function->nameIndex); |
511 | if (seenSignals.contains(value: slotName)) |
512 | return qQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Duplicate method name: invalid override of property change signal or superclass signal" )); |
513 | // Note: we don't append slotName to the seenSignals list, since we don't |
514 | // protect against overriding change signals or methods with properties. |
515 | |
516 | QList<QByteArray> parameterNames; |
517 | QVector<int> parameterTypes; |
518 | auto formal = function->formalsBegin(); |
519 | auto end = function->formalsEnd(); |
520 | for ( ; formal != end; ++formal) { |
521 | flags.setHasArguments(true); |
522 | parameterNames << stringAt(index: formal->nameIndex).toUtf8(); |
523 | int type = metaTypeForParameter(param: formal->type); |
524 | if (type == QMetaType::UnknownType) |
525 | type = QMetaType::QVariant; |
526 | parameterTypes << type; |
527 | } |
528 | |
529 | int returnType = metaTypeForParameter(param: function->returnType); |
530 | if (returnType == QMetaType::UnknownType) |
531 | returnType = QMetaType::QVariant; |
532 | |
533 | cache->appendMethod(slotName, flags, coreIndex: effectiveMethodIndex++, returnType, names: parameterNames, parameterTypes); |
534 | } |
535 | |
536 | |
537 | // Dynamic properties |
538 | int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; |
539 | int propertyIdx = 0; |
540 | p = obj->propertiesBegin(); |
541 | pend = obj->propertiesEnd(); |
542 | for ( ; p != pend; ++p, ++propertyIdx) { |
543 | int propertyType = 0; |
544 | int propertTypeMinorVersion = 0; |
545 | QQmlPropertyData::Flags propertyFlags; |
546 | |
547 | const QV4::CompiledData::BuiltinType type = p->builtinType(); |
548 | |
549 | if (type == QV4::CompiledData::BuiltinType::Var) |
550 | propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType; |
551 | |
552 | |
553 | if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) { |
554 | propertyType = metaTypeForPropertyType(type); |
555 | |
556 | if (type == QV4::CompiledData::BuiltinType::Variant) |
557 | propertyFlags.type = QQmlPropertyData::Flags::QVariantType; |
558 | } else { |
559 | Q_ASSERT(!p->isBuiltinType); |
560 | |
561 | QQmlType qmltype; |
562 | bool selfReference = false; |
563 | if (!imports->resolveType(stringAt(index: p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr, |
564 | nullptr, QQmlType::AnyRegistrationType, &selfReference)) { |
565 | return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid property type" )); |
566 | } |
567 | |
568 | // inline components are not necessarily valid yet |
569 | Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType()); |
570 | if (qmltype.isComposite() || qmltype.isInlineComponentType()) { |
571 | CompositeMetaTypeIds typeIds; |
572 | if (qmltype.isInlineComponentType()) { |
573 | auto objectId = qmltype.inlineComponendId(); |
574 | auto containingType = qmltype.containingType(); |
575 | if (containingType.isValid()) { |
576 | auto icType = containingType.lookupInlineComponentById(objectid: objectId); |
577 | typeIds = {icType.typeId(), icType.qListTypeId()}; |
578 | } else { |
579 | typeIds = {}; |
580 | } |
581 | if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type |
582 | typeIds = objectContainer->typeIdsForComponent(objectId); |
583 | Q_ASSERT(typeIds.isValid()); |
584 | } else if (selfReference) { |
585 | typeIds = objectContainer->typeIdsForComponent(); |
586 | } else { |
587 | QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(unNormalizedUrl: qmltype.sourceUrl()); |
588 | Q_ASSERT(tdata); |
589 | Q_ASSERT(tdata->isComplete()); |
590 | |
591 | auto compilationUnit = tdata->compilationUnit(); |
592 | typeIds = compilationUnit->typeIdsForComponent(); |
593 | } |
594 | |
595 | if (p->isList) { |
596 | propertyType = typeIds.listId; |
597 | } else { |
598 | propertyType = typeIds.id; |
599 | } |
600 | } else { |
601 | if (p->isList) { |
602 | propertyType = qmltype.qListTypeId(); |
603 | } else { |
604 | propertyType = qmltype.typeId(); |
605 | propertTypeMinorVersion = qmltype.minorVersion(); |
606 | } |
607 | } |
608 | |
609 | if (p->isList) |
610 | propertyFlags.type = QQmlPropertyData::Flags::QListType; |
611 | else |
612 | propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; |
613 | } |
614 | |
615 | if (!p->isReadOnly && !p->isList) |
616 | propertyFlags.setIsWritable(true); |
617 | |
618 | |
619 | QString propertyName = stringAt(index: p->nameIndex); |
620 | if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) |
621 | cache->_defaultPropertyName = propertyName; |
622 | cache->appendProperty(propertyName, flags: propertyFlags, coreIndex: effectivePropertyIndex++, |
623 | propType: propertyType, revision: propertTypeMinorVersion, notifyIndex: effectiveSignalIndex); |
624 | |
625 | effectiveSignalIndex++; |
626 | } |
627 | |
628 | QQmlError noError; |
629 | return noError; |
630 | } |
631 | |
632 | template <typename ObjectContainer> |
633 | inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, |
634 | QString *customTypeName) |
635 | { |
636 | if (param.indexIsBuiltinType) { |
637 | // built-in type |
638 | return metaTypeForPropertyType(type: static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType))); |
639 | } |
640 | |
641 | // lazily resolved type |
642 | const QString typeName = stringAt(index: param.typeNameIndexOrBuiltinType); |
643 | if (customTypeName) |
644 | *customTypeName = typeName; |
645 | QQmlType qmltype; |
646 | bool selfReference = false; |
647 | if (!imports->resolveType(type: typeName, type_return: &qmltype, version_major: nullptr, version_minor: nullptr, ns_return: nullptr, errors: nullptr, registrationType: QQmlType::AnyRegistrationType, |
648 | typeRecursionDetected: &selfReference)) |
649 | return QMetaType::UnknownType; |
650 | |
651 | if (!qmltype.isComposite()) |
652 | return qmltype.typeId(); |
653 | |
654 | if (selfReference) |
655 | return objectContainer->typeIdsForComponent().id; |
656 | |
657 | QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(unNormalizedUrl: qmltype.sourceUrl()); |
658 | Q_ASSERT(tdata); |
659 | Q_ASSERT(tdata->isComplete()); |
660 | |
661 | auto compilationUnit = tdata->compilationUnit(); |
662 | |
663 | return compilationUnit->metaTypeId; |
664 | } |
665 | |
666 | template <typename ObjectContainer> |
667 | class QQmlPropertyCacheAliasCreator |
668 | { |
669 | public: |
670 | typedef typename ObjectContainer::CompiledObject CompiledObject; |
671 | |
672 | QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); |
673 | |
674 | void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv); |
675 | |
676 | QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv); |
677 | |
678 | private: |
679 | void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv); |
680 | QQmlError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv); |
681 | |
682 | void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; |
683 | |
684 | int objectForId(const CompiledObject &component, int id) const; |
685 | |
686 | QQmlPropertyCacheVector *propertyCaches; |
687 | const ObjectContainer *objectContainer; |
688 | }; |
689 | |
690 | template <typename ObjectContainer> |
691 | inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) |
692 | : propertyCaches(propertyCaches) |
693 | , objectContainer(objectContainer) |
694 | { |
695 | |
696 | } |
697 | |
698 | template <typename ObjectContainer> |
699 | inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv) |
700 | { |
701 | // skip the root object (index 0) as that one does not have a first object index originating |
702 | // from a binding. |
703 | for (int i = 1; i < objectContainer->objectCount(); ++i) { |
704 | const CompiledObject &component = *objectContainer->objectAt(i); |
705 | if (!(component.flags & QV4::CompiledData::Object::IsComponent)) |
706 | continue; |
707 | |
708 | const auto rootBinding = component.bindingsBegin(); |
709 | appendAliasPropertiesInMetaObjectsWithinComponent(component, firstObjectIndex: rootBinding->value.objectIndex, enginePriv); |
710 | } |
711 | |
712 | const int rootObjectIndex = 0; |
713 | appendAliasPropertiesInMetaObjectsWithinComponent(component: *objectContainer->objectAt(rootObjectIndex), firstObjectIndex: rootObjectIndex, enginePriv); |
714 | } |
715 | |
716 | template <typename ObjectContainer> |
717 | inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv) |
718 | { |
719 | QVector<int> objectsWithAliases; |
720 | collectObjectsWithAliasesRecursively(objectIndex: firstObjectIndex, objectsWithAliases: &objectsWithAliases); |
721 | if (objectsWithAliases.isEmpty()) |
722 | return; |
723 | |
724 | const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) { |
725 | auto alias = object.aliasesBegin(); |
726 | auto end = object.aliasesEnd(); |
727 | for ( ; alias != end; ++alias) { |
728 | Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); |
729 | |
730 | const int targetObjectIndex = objectForId(component, id: alias->targetObjectId); |
731 | Q_ASSERT(targetObjectIndex >= 0); |
732 | |
733 | if (alias->aliasToLocalAlias) |
734 | continue; |
735 | |
736 | if (alias->encodedMetaPropertyIndex == -1) |
737 | continue; |
738 | |
739 | const QQmlPropertyCache *targetCache = propertyCaches->at(index: targetObjectIndex); |
740 | Q_ASSERT(targetCache); |
741 | |
742 | int coreIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias->encodedMetaPropertyIndex).coreIndex(); |
743 | QQmlPropertyData *targetProperty = targetCache->property(index: coreIndex); |
744 | if (!targetProperty) |
745 | return false; |
746 | } |
747 | return true; |
748 | }; |
749 | |
750 | do { |
751 | QVector<int> pendingObjects; |
752 | |
753 | for (int objectIndex: qAsConst(t&: objectsWithAliases)) { |
754 | const CompiledObject &object = *objectContainer->objectAt(objectIndex); |
755 | |
756 | if (allAliasTargetsExist(object)) { |
757 | appendAliasesToPropertyCache(component, objectIndex, enginePriv); |
758 | } else { |
759 | pendingObjects.append(t: objectIndex); |
760 | } |
761 | |
762 | } |
763 | qSwap(value1&: objectsWithAliases, value2&: pendingObjects); |
764 | } while (!objectsWithAliases.isEmpty()); |
765 | } |
766 | |
767 | template <typename ObjectContainer> |
768 | inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const |
769 | { |
770 | const CompiledObject &object = *objectContainer->objectAt(objectIndex); |
771 | if (object.aliasCount() > 0) |
772 | objectsWithAliases->append(t: objectIndex); |
773 | |
774 | // Stop at Component boundary |
775 | if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) |
776 | return; |
777 | |
778 | auto binding = object.bindingsBegin(); |
779 | auto end = object.bindingsEnd(); |
780 | for (; binding != end; ++binding) { |
781 | if (binding->type != QV4::CompiledData::Binding::Type_Object |
782 | && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty |
783 | && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) |
784 | continue; |
785 | |
786 | collectObjectsWithAliasesRecursively(objectIndex: binding->value.objectIndex, objectsWithAliases); |
787 | } |
788 | } |
789 | |
790 | template <typename ObjectContainer> |
791 | inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, |
792 | QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv) |
793 | { |
794 | *type = 0; |
795 | bool writable = false; |
796 | bool resettable = false; |
797 | |
798 | propertyFlags->setIsAlias(true); |
799 | |
800 | if (alias.aliasToLocalAlias) { |
801 | const QV4::CompiledData::Alias *lastAlias = &alias; |
802 | QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias}); |
803 | |
804 | do { |
805 | const int targetObjectIndex = objectForId(component, id: lastAlias->targetObjectId); |
806 | Q_ASSERT(targetObjectIndex >= 0); |
807 | const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex); |
808 | Q_ASSERT(targetObject); |
809 | |
810 | auto nextAlias = targetObject->aliasesBegin(); |
811 | for (uint i = 0; i < lastAlias->localAliasIndex; ++i) |
812 | ++nextAlias; |
813 | |
814 | const QV4::CompiledData::Alias *targetAlias = &(*nextAlias); |
815 | if (seenAliases.contains(t: targetAlias)) { |
816 | return qQmlCompileError(location: targetAlias->location, |
817 | description: QQmlPropertyCacheCreatorBase::tr(sourceText: "Cyclic alias" )); |
818 | } |
819 | |
820 | seenAliases.append(t: targetAlias); |
821 | lastAlias = targetAlias; |
822 | } while (lastAlias->aliasToLocalAlias); |
823 | |
824 | return propertyDataForAlias(component, alias: *lastAlias, type, minorVersion, propertyFlags, enginePriv); |
825 | } |
826 | |
827 | const int targetObjectIndex = objectForId(component, id: alias.targetObjectId); |
828 | Q_ASSERT(targetObjectIndex >= 0); |
829 | const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); |
830 | |
831 | if (alias.encodedMetaPropertyIndex == -1) { |
832 | Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); |
833 | auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex); |
834 | if (!typeRef) { |
835 | // Can be caused by the alias target not being a valid id or property. E.g.: |
836 | // property alias dataValue: dataVal |
837 | // invalidAliasComponent { id: dataVal } |
838 | return qQmlCompileError(targetObject.location, |
839 | QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid alias target" )); |
840 | } |
841 | |
842 | if (typeRef->type.isValid()) |
843 | *type = typeRef->type.typeId(); |
844 | else |
845 | *type = typeRef->compilationUnit()->metaTypeId; |
846 | |
847 | *minorVersion = typeRef->minorVersion; |
848 | |
849 | propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType; |
850 | } else { |
851 | int coreIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias.encodedMetaPropertyIndex).coreIndex(); |
852 | int valueTypeIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias.encodedMetaPropertyIndex).valueTypeIndex(); |
853 | |
854 | QQmlPropertyCache *targetCache = propertyCaches->at(index: targetObjectIndex); |
855 | Q_ASSERT(targetCache); |
856 | |
857 | QQmlPropertyData *targetProperty = targetCache->property(index: coreIndex); |
858 | Q_ASSERT(targetProperty); |
859 | |
860 | // for deep aliases, valueTypeIndex is always set |
861 | if (!QQmlValueTypeFactory::isValueType(idx: targetProperty->propType()) && valueTypeIndex != -1) { |
862 | // deep alias property |
863 | *type = targetProperty->propType(); |
864 | targetCache = enginePriv->propertyCacheForType(*type); |
865 | Q_ASSERT(targetCache); |
866 | targetProperty = targetCache->property(index: valueTypeIndex); |
867 | |
868 | if (targetProperty == nullptr) { |
869 | return qQmlCompileError(location: alias.referenceLocation, |
870 | description: QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid alias target" )); |
871 | } |
872 | |
873 | *type = targetProperty->propType(); |
874 | writable = targetProperty->isWritable(); |
875 | resettable = targetProperty->isResettable(); |
876 | |
877 | } else { |
878 | // value type or primitive type or enum |
879 | *type = targetProperty->propType(); |
880 | |
881 | writable = targetProperty->isWritable(); |
882 | resettable = targetProperty->isResettable(); |
883 | |
884 | if (valueTypeIndex != -1) { |
885 | const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: *type); |
886 | if (valueTypeMetaObject->property(index: valueTypeIndex).isEnumType()) |
887 | *type = QMetaType::Int; |
888 | else |
889 | *type = valueTypeMetaObject->property(index: valueTypeIndex).userType(); |
890 | } else { |
891 | if (targetProperty->isEnum()) { |
892 | *type = QMetaType::Int; |
893 | } else { |
894 | // Copy type flags |
895 | propertyFlags->copyPropertyTypeFlags(from: targetProperty->flags()); |
896 | |
897 | if (targetProperty->isVarProperty()) |
898 | propertyFlags->type = QQmlPropertyData::Flags::QVariantType; |
899 | } |
900 | } |
901 | } |
902 | } |
903 | |
904 | propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable); |
905 | propertyFlags->setIsResettable(resettable); |
906 | return QQmlError(); |
907 | } |
908 | |
909 | template <typename ObjectContainer> |
910 | inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( |
911 | const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv) |
912 | { |
913 | const CompiledObject &object = *objectContainer->objectAt(objectIndex); |
914 | if (!object.aliasCount()) |
915 | return QQmlError(); |
916 | |
917 | QQmlPropertyCache *propertyCache = propertyCaches->at(index: objectIndex); |
918 | Q_ASSERT(propertyCache); |
919 | |
920 | int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); |
921 | int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); |
922 | |
923 | int aliasIndex = 0; |
924 | auto alias = object.aliasesBegin(); |
925 | auto end = object.aliasesEnd(); |
926 | for ( ; alias != end; ++alias, ++aliasIndex) { |
927 | Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); |
928 | |
929 | int type = 0; |
930 | int minorVersion = 0; |
931 | QQmlPropertyData::Flags propertyFlags; |
932 | QQmlError error = propertyDataForAlias(component, alias: *alias, type: &type, minorVersion: &minorVersion, propertyFlags: &propertyFlags, enginePriv); |
933 | if (error.isValid()) |
934 | return error; |
935 | |
936 | const QString propertyName = objectContainer->stringAt(alias->nameIndex); |
937 | |
938 | if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) |
939 | propertyCache->_defaultPropertyName = propertyName; |
940 | |
941 | propertyCache->appendProperty(propertyName, flags: propertyFlags, coreIndex: effectivePropertyIndex++, |
942 | propType: type, revision: minorVersion, notifyIndex: effectiveSignalIndex++); |
943 | } |
944 | |
945 | return QQmlError(); |
946 | } |
947 | |
948 | template <typename ObjectContainer> |
949 | inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const |
950 | { |
951 | for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) { |
952 | const int candidateIndex = component.namedObjectsInComponentTable()[i]; |
953 | const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex); |
954 | if (candidate.id == id) |
955 | return candidateIndex; |
956 | } |
957 | return -1; |
958 | } |
959 | |
960 | QT_END_NAMESPACE |
961 | |
962 | #endif // QQMLPROPERTYCACHECREATOR_P_H |
963 | |