Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmltypecompiler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <private/qqmlobjectcreator_p.h>
8#include <private/qqmlcustomparser_p.h>
9#include <private/qqmlvmemetaobject_p.h>
10#include <private/qqmlcomponent_p.h>
11#include <private/qqmlpropertyresolver_p.h>
12#include <private/qqmlcomponentandaliasresolver_p.h>
13#include <private/qqmlsignalnames_p.h>
14
15#define COMPILE_EXCEPTION(token, desc)
16 {
17 recordError((token)->location, desc);
18 return false;
19 }
20
22
23DEFINE_BOOL_CONFIG_OPTION(
24 disableInternalDeferredProperties, QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES);
25
26Q_LOGGING_CATEGORY(lcQmlTypeCompiler, "qt.qml.typecompiler");
27
28QQmlTypeCompiler::QQmlTypeCompiler(
29 QQmlTypeLoader *typeLoader, QQmlTypeData *typeData, QmlIR::Document *parsedQML,
30 QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
31 const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
32 : resolvedTypes(resolvedTypeCache)
33 , loader(typeLoader)
34 , dependencyHasher(dependencyHasher)
35 , document(parsedQML)
36 , typeData(typeData)
37{
38}
39
41{
42 // Build property caches and VME meta object data
43
44 for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd();
45 it != end; ++it) {
46 QQmlCustomParser *customParser = (*it)->type().customParser();
47 if (customParser)
48 customParsers.insert(it.key(), customParser);
49 }
50
51 QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
52
53
54 {
55 QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(
56 &m_propertyCaches, &pendingGroupPropertyBindings,
57 loader, this, imports(), typeData->typeClassName());
58 QQmlError cycleError = propertyCacheBuilder.verifyNoICCycle();
59 if (cycleError.isValid()) {
60 recordError(cycleError);
61 return nullptr;
62 }
63 QQmlPropertyCacheCreatorBase::IncrementalResult result;
64 do {
65 result = propertyCacheBuilder.buildMetaObjectsIncrementally();
66 const QQmlError &error = result.error;
67 if (error.isValid()) {
68 recordError(error);
69 return nullptr;
70 } else {
71 // Resolve component boundaries and aliases
72
73 QQmlComponentAndAliasResolver resolver(this, &m_propertyCaches);
74 if (QQmlError error = resolver.resolve(result.processedRoot); error.isValid()) {
75 recordError(error);
76 return nullptr;
77 }
78 pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_propertyCaches);
79 pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
80 }
81 } while (result.canResume);
82 }
83
84 {
87 }
88
89 {
90 SignalHandlerResolver converter(this);
92 return nullptr;
93 }
94
95 {
96 QQmlEnumTypeResolver enumResolver(this);
97 if (!enumResolver.resolveEnumBindings())
98 return nullptr;
99 }
100
101 {
104 }
105
106 {
107 QQmlAliasAnnotator annotator(this);
109 }
110
111 {
112 QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this);
113 if (!deferredAndCustomParserBindingScanner.scanObject())
114 return nullptr;
115 }
116
117 if (!document->javaScriptCompilationUnit || !document->javaScriptCompilationUnit->unitData()) {
118 // Compile JS binding expressions and signal handlers if necessary
119 {
120 // We can compile script strings ahead of time, but they must be compiled
121 // without type optimizations as their scope is always entirely dynamic.
123 sss.scan();
124 }
125
126 Q_ASSERT(document->jsModule.fileName == typeData->urlString());
127 Q_ASSERT(document->jsModule.finalUrl == typeData->finalUrlString());
128 QmlIR::JSCodeGen v4CodeGenerator(document);
129 for (QmlIR::Object *object : std::as_const(document->objects)) {
130 if (!v4CodeGenerator.generateRuntimeFunctions(object)) {
131 Q_ASSERT(v4CodeGenerator.hasError());
132 recordError(v4CodeGenerator.error());
133 return nullptr;
134 }
135 }
136 document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
137 }
138
139 // Generate QML compiled type data structures
140
141 QmlIR::QmlUnitGenerator qmlGenerator;
142 qmlGenerator.generate(*document, dependencyHasher);
143
144 if (!errors.isEmpty())
145 return nullptr;
146
147 return std::move(document->javaScriptCompilationUnit);
148}
149
150void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
151{
152 QQmlError error;
153 error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
154 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
155 error.setDescription(description);
156 error.setUrl(url());
157 errors << error;
158}
159
160void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message)
161{
162 QQmlError error;
163 error.setDescription(message.message);
164 error.setLine(qmlConvertSourceCoordinate<quint32, int>(message.loc.startLine));
165 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(message.loc.startColumn));
166 error.setUrl(url());
167 errors << error;
168}
169
170void QQmlTypeCompiler::recordError(const QQmlError &e)
171{
172 QQmlError error = e;
173 error.setUrl(url());
174 errors << error;
175}
176
178{
179 return document->stringAt(idx);
180}
181
182int QQmlTypeCompiler::registerString(const QString &str)
183{
184 return document->jsGenerator.registerString(str);
185}
186
187int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
188{
189 return document->jsGenerator.registerConstant(v);
190}
191
193{
194 return document->javaScriptCompilationUnit->unitData();
195}
196
197const QQmlImports *QQmlTypeCompiler::imports() const
198{
199 return typeData->imports();
200}
201
203{
204 return &document->objects;
205}
206
208{
209 return &m_propertyCaches;
210}
211
213{
214 return &m_propertyCaches;
215}
216
218{
219 return document->jsParserEngine.pool();
220}
221
223{
224 return document->jsParserEngine.newStringRef(string);
225}
226
228{
229 return &document->jsGenerator.stringTable;
230}
231
232QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
233{
234 return object->bindingAsString(document, scriptIndex);
235}
236
237void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, QTypeRevision version)
238{
239 const quint32 moduleIdx = registerString(module);
240 const quint32 qualifierIdx = registerString(qualifier);
241
242 for (int i = 0, count = document->imports.size(); i < count; ++i) {
243 const QV4::CompiledData::Import *existingImport = document->imports.at(i);
244 if (existingImport->type == QV4::CompiledData::Import::ImportLibrary
245 && existingImport->uriIndex == moduleIdx
246 && existingImport->qualifierIndex == qualifierIdx)
247 return;
248 }
249 auto pool = memoryPool();
250 QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
251 import->type = QV4::CompiledData::Import::ImportLibrary;
252 import->version = version;
253 import->uriIndex = moduleIdx;
254 import->qualifierIndex = qualifierIdx;
255 document->imports.append(import);
256}
257
258QQmlType QQmlTypeCompiler::qmlTypeForComponent(const QString &inlineComponentName) const
259{
260 return typeData->qmlType(inlineComponentName);
261}
262
264 : compiler(typeCompiler)
265{
266}
267
268SignalHandlerResolver::SignalHandlerResolver(QQmlTypeCompiler *typeCompiler)
269 : QQmlCompilePass(typeCompiler)
270 , typeLoader(typeCompiler->typeLoader())
271 , qmlObjects(*typeCompiler->qmlObjects())
272 , imports(typeCompiler->imports())
273 , customParsers(typeCompiler->customParserCache())
274 , propertyCaches(typeCompiler->propertyCaches())
275{
276}
277
279{
280 for (int objectIndex = 0; objectIndex < qmlObjects.size(); ++objectIndex) {
281 const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
282 QQmlPropertyCache::ConstPtr cache = propertyCaches->at(objectIndex);
283 if (!cache)
284 continue;
285 if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) {
286 if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers))
287 continue;
288 }
289 const QString elementName = stringAt(obj->inheritedTypeNameIndex);
290 if (!resolveSignalHandlerExpressions(obj, elementName, cache))
291 return false;
292 }
293 return true;
294}
295
296bool SignalHandlerResolver::resolveSignalHandlerExpressions(
297 const QmlIR::Object *obj, const QString &typeName,
298 const QQmlPropertyCache::ConstPtr &propertyCache,
299 QQmlPropertyResolver::RevisionCheck checkRevision)
300{
301 // map from signal name defined in qml itself to list of parameters
302 QHash<QString, QStringList> customSignals;
303
304 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
305 const QString bindingPropertyName = stringAt(binding->propertyNameIndex);
306 // Attached property?
307 const QV4::CompiledData::Binding::Type bindingType = binding->type();
308 if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
309 const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
310 auto *typeRef = resolvedType(binding->propertyNameIndex);
311 QQmlType type = typeRef ? typeRef->type() : QQmlType();
312 if (!type.isValid())
313 imports->resolveType(typeLoader, bindingPropertyName, &type, nullptr, nullptr);
314
315 const QMetaObject *attachedType = type.attachedPropertiesType(typeLoader);
316 if (!attachedType)
317 COMPILE_EXCEPTION(binding, tr("Non-existent attached object"));
318 QQmlPropertyCache::ConstPtr cache = QQmlMetaType::propertyCache(attachedType);
319
320 // Ignore revisions of signals on attached objects. They are not unqualified.
322 attachedObj, bindingPropertyName, cache,
323 QQmlPropertyResolver::IgnoreRevision)) {
324 return false;
325 }
326
327 continue;
328 }
329
330 QString qPropertyName;
331 QString signalName;
332 if (auto propertyName =
333 QQmlSignalNames::changedHandlerNameToPropertyName(bindingPropertyName)) {
334 qPropertyName = *propertyName;
335 signalName = *QQmlSignalNames::changedHandlerNameToSignalName(bindingPropertyName);
336 } else {
337 signalName = QQmlSignalNames::handlerNameToSignalName(bindingPropertyName)
338 .value_or(QString());
339 }
340 if (signalName.isEmpty())
341 continue;
342
343 QQmlPropertyResolver resolver(propertyCache);
344
345 bool notInRevision = false;
346 const QQmlPropertyData *const signal
347 = resolver.signal(signalName, &notInRevision, checkRevision);
348 const QQmlPropertyData *const signalPropertyData
349 = resolver.property(signalName, /*notInRevision ptr*/nullptr, checkRevision);
350 const QQmlPropertyData *const qPropertyData = !qPropertyName.isEmpty()
351 ? resolver.property(qPropertyName, nullptr, checkRevision)
352 : nullptr;
353 QString finalSignalHandlerPropertyName = signalName;
354 QV4::CompiledData::Binding::Flag flag
355 = QV4::CompiledData::Binding::IsSignalHandlerExpression;
356
357 const bool isPropertyObserver
358 = !signalPropertyData && qPropertyData && qPropertyData->notifiesViaBindable();
359 if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) {
360 int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
361 sigIndex = propertyCache->originalClone(sigIndex);
362
363 bool unnamedParameter = false;
364
365 QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex);
366 for (int i = 0; i < parameterNames.size(); ++i) {
367 const QString param = QString::fromUtf8(parameterNames.at(i));
368 if (param.isEmpty())
369 unnamedParameter = true;
370 else if (unnamedParameter) {
371 COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter."));
372 } else if (QV4::Compiler::Codegen::isNameGlobal(param)) {
373 COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param));
374 }
375 }
376 } else if (isPropertyObserver) {
377 finalSignalHandlerPropertyName = qPropertyName;
378 flag = QV4::CompiledData::Binding::IsPropertyObserver;
379 } else {
380 if (notInRevision) {
381 // Try assinging it as a property later
382 if (signalPropertyData)
383 continue;
384
385 const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
386
387 auto *typeRef = resolvedType(obj->inheritedTypeNameIndex);
388 const QQmlType type = typeRef ? typeRef->type() : QQmlType();
389 if (type.isValid()) {
390 COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.")
391 .arg(typeName, originalPropertyName, type.module())
392 .arg(type.version().majorVersion())
393 .arg(type.version().minorVersion()));
394 } else {
396 binding,
397 tr("\"%1.%2\" is not available due to component versioning.")
398 .arg(typeName, originalPropertyName));
399 }
400 }
401
402 // Try to look up the signal parameter names in the object itself
403
404 // build cache if necessary
405 if (customSignals.isEmpty()) {
406 for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) {
407 const QString &signalName = stringAt(signal->nameIndex);
408 customSignals.insert(signalName, signal->parameterStringList(compiler->stringPool()));
409 }
410
411 for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) {
412 const QString propName = stringAt(property->nameIndex());
413 customSignals.insert(propName, QStringList());
414 }
415 }
416
417 QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(signalName);
418 if (entry == customSignals.constEnd() && !qPropertyName.isEmpty())
419 entry = customSignals.constFind(qPropertyName);
420
421 if (entry == customSignals.constEnd()) {
422 // Can't find even a custom signal, then just don't do anything and try
423 // keeping the binding as a regular property assignment.
424 continue;
425 }
426 }
427
428 // Binding object to signal means connect the signal to the object's default method.
429 if (bindingType == QV4::CompiledData::Binding::Type_Object) {
430 binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerObject);
431 continue;
432 }
433
434 if (bindingType != QV4::CompiledData::Binding::Type_Script) {
435 if (bindingType < QV4::CompiledData::Binding::Type_Script) {
436 COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)"));
437 } else {
438 COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment"));
439 }
440 }
441
442 binding->propertyNameIndex = compiler->registerString(finalSignalHandlerPropertyName);
443 binding->setFlag(flag);
444 }
445 return true;
446}
447
448QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
449 : QQmlCompilePass(typeCompiler)
450 , qmlObjects(*typeCompiler->qmlObjects())
451 , propertyCaches(typeCompiler->propertyCaches())
452 , imports(typeCompiler->imports())
453{
454}
455
457{
458 for (int i = 0; i < qmlObjects.size(); ++i) {
459 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i);
460 if (!propertyCache)
461 continue;
462 const QmlIR::Object *obj = qmlObjects.at(i);
463
464 QQmlPropertyResolver resolver(propertyCache);
465
466 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
467 const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
468 if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
469 || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject
470 || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
471 continue;
472
473 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
474 continue;
475
476 const QString propertyName = stringAt(binding->propertyNameIndex);
477 bool notInRevision = false;
478 const QQmlPropertyData *pd = resolver.property(propertyName, &notInRevision);
479 if (!pd || pd->isQList())
480 continue;
481
482 if (!pd->isEnum() && pd->propType().id() != QMetaType::Int)
483 continue;
484
485 if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding))
486 return false;
487 }
488 }
489
490 return true;
491}
492
493bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, QStringView, int enumValue, bool)
494{
495 binding->setType(QV4::CompiledData::Binding::Type_Number);
496 binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
497// binding->setNumberValueInternal((double)enumValue);
498 binding->setFlag(QV4::CompiledData::Binding::IsResolvedEnum);
499 return true;
500}
501
502bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(
503 const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache,
504 const QQmlPropertyData *prop, QmlIR::Binding *binding)
505{
506 bool isIntProp = (prop->propType().id() == QMetaType::Int) && !prop->isEnum();
507 if (!prop->isEnum() && !isIntProp)
508 return true;
509
510 if (!prop->isWritable()
511 && !(binding->hasFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))) {
512 COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property")
513 .arg(stringAt(binding->propertyNameIndex)));
514 }
515
516 Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
517 const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
518 if (!string.constData()->isUpper())
519 return true;
520
521 // reject any "complex" expression (even simple arithmetic)
522 // we do this by excluding everything that is not part of a
523 // valid identifier or a dot
524 for (const QChar &c : string)
525 if (!(c.isLetterOrNumber() || c == u'.' || c == u'_' || c.isSpace()))
526 return true;
527
528 // we support one or two '.' in the enum phrase:
529 // * <TypeName>.<EnumValue>
530 // * <TypeName>.<ScopedEnumName>.<EnumValue>
531
532 int dot = string.indexOf(QLatin1Char('.'));
533 if (dot == -1 || dot == string.size()-1)
534 return true;
535
536 int dot2 = string.indexOf(QLatin1Char('.'), dot+1);
537 if (dot2 != -1 && dot2 != string.size()-1) {
538 if (!string.at(dot+1).isUpper())
539 return true;
540 if (string.indexOf(QLatin1Char('.'), dot2+1) != -1)
541 return true;
542 }
543
544 QHashedStringRef typeName(string.constData(), dot);
545 const bool isQtObject = (typeName == QLatin1String("Qt"));
546 const QStringView scopedEnumName = (dot2 != -1 ? QStringView{string}.mid(dot + 1, dot2 - dot - 1) : QStringView());
547 // ### consider supporting scoped enums in Qt namespace
548 const QStringView enumValue = QStringView{string}.mid(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1);
549
550 if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here?
551 // Allow enum assignment to ints.
552 bool ok;
553 int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok);
554 if (ok) {
555 if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject))
556 return false;
557 }
558 return true;
559 }
560 QQmlType type;
561 imports->resolveType(compiler->typeLoader(), typeName, &type, nullptr, nullptr);
562
563 if (!type.isValid() && !isQtObject)
564 return true;
565
566 int value = 0;
567 bool ok = false;
568
569 auto *tr = resolvedType(obj->inheritedTypeNameIndex);
570
571 // When these two match, we can short cut the search, unless...
572 bool useFastPath = type.isValid() && tr && tr->type() == type;
573 QMetaProperty mprop;
574 QMetaEnum menum;
575 if (useFastPath) {
576 mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
577 menum = mprop.enumerator();
578 // ...the enumerator merely comes from a related metaobject, but the enum scope does not match
579 // the typename we resolved
580 if (!menum.isScoped() && scopedEnumName.isEmpty() && typeName != QString::fromUtf8(menum.scope()))
581 useFastPath = false;;
582 }
583 if (useFastPath) {
584 QByteArray enumName = enumValue.toUtf8();
585 if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8())
586 return true;
587
588 if (mprop.isFlagType()) {
589 value = menum.keysToValue(enumName.constData(), &ok);
590 } else {
591 value = menum.keyToValue(enumName.constData(), &ok);
592 }
593 } else {
594 // Otherwise we have to search the whole type
595 if (type.isValid()) {
596 if (!scopedEnumName.isEmpty()) {
597 value = type.scopedEnumValue(
598 compiler->typeLoader(), scopedEnumName, enumValue, &ok);
599 } else {
600 value = type.enumValue(compiler->typeLoader(), QHashedStringRef(enumValue), &ok);
601 }
602 } else {
603 QByteArray enumName = enumValue.toUtf8();
604 const QMetaObject *metaObject = &Qt::staticMetaObject;
605 for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
606 QMetaEnum e = metaObject->enumerator(ii);
607 value = e.keyToValue(enumName.constData(), &ok);
608 }
609 }
610 }
611
612 if (!ok)
613 return true;
614
615 return assignEnumToBinding(binding, enumValue, value, isQtObject);
616}
617
618int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, QStringView enumName, QStringView enumValue, bool *ok) const
619{
620 Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer");
621 *ok = false;
622
623 if (scope != QLatin1String("Qt")) {
624 QQmlType type;
625 imports->resolveType(compiler->typeLoader(), scope, &type, nullptr, nullptr);
626 if (!type.isValid())
627 return -1;
628 if (!enumName.isEmpty())
629 return type.scopedEnumValue(compiler->typeLoader(), enumName, enumValue, ok);
630 return type.enumValue(
631 compiler->typeLoader(),
632 QHashedStringRef(enumValue.constData(), enumValue.size()), ok);
633 }
634
635 const QMetaObject *mo = &Qt::staticMetaObject;
636 int i = mo->enumeratorCount();
637 const QByteArray ba = enumValue.toUtf8();
638 while (i--) {
639 int v = mo->enumerator(i).keyToValue(ba.constData(), ok);
640 if (*ok)
641 return v;
642 }
643 return -1;
644}
645
652
654{
655 scanObjectRecursively(/*root object*/0);
656 for (int i = 0; i < qmlObjects.size(); ++i)
657 if (qmlObjects.at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
658 scanObjectRecursively(i);
659}
660
661void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings)
662{
663 const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
664 if (!annotateScriptBindings)
665 annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex);
666 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
667 switch (binding->type()) {
668 case QV4::CompiledData::Binding::Type_Script:
669 if (annotateScriptBindings) {
670 binding->stringIndex = compiler->registerString(
671 compiler->bindingAsString(obj, binding->value.compiledScriptIndex));
672 }
673 break;
674 case QV4::CompiledData::Binding::Type_Object:
675 case QV4::CompiledData::Binding::Type_AttachedProperty:
676 case QV4::CompiledData::Binding::Type_GroupProperty:
677 scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings);
678 break;
679 default:
680 break;
681 }
682 }
683}
684
691
693{
694 for (int i = 0; i < qmlObjects.size(); ++i) {
695 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i);
696 if (!propertyCache)
697 continue;
698
699 const QmlIR::Object *obj = qmlObjects.at(i);
700
701 QQmlPropertyResolver resolver(propertyCache);
702 const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
703
704 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
705 if (!binding->isValueBinding())
706 continue;
707 bool notInRevision = false;
708 const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
709 if (pd && pd->isAlias())
710 binding->setFlag(QV4::CompiledData::Binding::IsBindingToAlias);
711 }
712 }
713}
714
722
724{
725 const QMetaType scriptStringMetaType = QMetaType::fromType<QQmlScriptString>();
726 for (int i = 0; i < qmlObjects.size(); ++i) {
727 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i);
728 if (!propertyCache)
729 continue;
730
731 const QmlIR::Object *obj = qmlObjects.at(i);
732
733 QQmlPropertyResolver resolver(propertyCache);
734 const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
735
736 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
737 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
738 continue;
739 bool notInRevision = false;
740 const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
741 if (!pd || pd->propType() != scriptStringMetaType)
742 continue;
743
744 QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
745 binding->stringIndex = compiler->registerString(script);
746 }
747 }
748}
749
750template<>
751void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::allocateNamedObjects(
752 QmlIR::Object *object) const
753{
754 object->namedObjectsInComponent.allocate(m_compiler->memoryPool(), m_idToObjectIndex);
755}
756
757template<>
758bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::markAsComponent(int index) const
759{
760 m_compiler->qmlObjects()->at(index)->flags |= QV4::CompiledData::Object::IsComponent;
761 return true;
762}
763
764template<>
765void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::setObjectId(int index) const
766{
767 m_compiler->qmlObjects()->at(index)->id = m_idToObjectIndex.size();
768}
769
770template<>
771bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::wrapImplicitComponent(QmlIR::Binding *binding)
772{
773 QQmlJS::MemoryPool *pool = m_compiler->memoryPool();
774 QList<QmlIR::Object *> *qmlObjects = m_compiler->qmlObjects();
775
776 // emulate "import QML 1.0" and then wrap the component in "QML.Component {}"
777 QQmlType componentType = QQmlMetaType::qmlType(
778 &QQmlComponent::staticMetaObject, QStringLiteral("QML"),
779 QTypeRevision::fromVersion(1, 0));
780 Q_ASSERT(componentType.isValid());
781 const QString qualifier = QStringLiteral("QML");
782
783 m_compiler->addImport(componentType.module(), qualifier, componentType.version());
784
785 QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
786 syntheticComponent->init(
787 pool,
788 m_compiler->registerString(
789 qualifier + QLatin1Char('.') + componentType.elementName()),
790 m_compiler->registerString(QString()), binding->valueLocation);
791 syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
792
793 if (!m_compiler->resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
794 auto typeRef = new QV4::ResolvedTypeReference;
795 typeRef->setType(componentType);
796 typeRef->setVersion(componentType.version());
797 m_compiler->resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef);
798 }
799
800 qmlObjects->append(syntheticComponent);
801 const int componentIndex = qmlObjects->size() - 1;
802 // Keep property caches symmetric
803 QQmlPropertyCache::ConstPtr componentCache
804 = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
805 m_propertyCaches->append(componentCache);
806
807 QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
808 *syntheticBinding = *binding;
809
810 // The synthetic binding inside Component has no name. It's just "Component { Foo {} }".
811 syntheticBinding->propertyNameIndex = 0;
812
813 syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object);
814 QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
815 Q_ASSERT(error.isEmpty());
816 Q_UNUSED(error);
817
818 binding->value.objectIndex = componentIndex;
819
820 m_componentRoots.append(componentIndex);
821 return true;
822}
823
824template<>
825void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveGeneralizedGroupProperty(
826 const CompiledObject &component, CompiledBinding *binding)
827{
828 Q_UNUSED(component);
829 // We cannot make it fail here. It might be a custom-parsed property
830 const int targetObjectIndex = m_idToObjectIndex.value(binding->propertyNameIndex, -1);
831 if (targetObjectIndex != -1)
832 m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(targetObjectIndex));
833}
834
835/*!
836 \internal
837
838 Attempts to resolve a "deep alias" — an alias whose sub-property path
839 goes through a QObject property, an inline component binding, or another
840 alias. For example: \c{alias foo: target.groupProp.innerProp}
841
842 First searches the target object's bindings and aliases for \a property,
843 then looks up \a subProperty on the bound/aliased object's property cache.
844 If no binding or alias matches, falls back to looking up \a subProperty
845 on the declared type's property cache.
846
847 On success, updates \a propIdx with the resolved value-type index and
848 returns \c true.
849*/
851 QQmlTypeCompiler *compiler, const QmlIR::Object *targetObject,
852 QStringView property, QStringView subProperty,
853 QQmlPropertyIndex &propIdx, QMetaType targetPropertyType,
854 const QQmlPropertyCacheVector *propertyCaches,
855 const QMap<int, int> &idToObjectIndex)
856{
857 for (auto it = targetObject->bindingsBegin(), end = targetObject->bindingsEnd();
858 it != end; ++it) {
859 auto binding = *it;
860 if (compiler->stringAt(binding.propertyNameIndex) != property)
861 continue;
862 const auto &cache = propertyCaches->at(binding.value.objectIndex);
863 if (!cache)
864 continue;
865 QQmlPropertyResolver resolver(cache);
866 const QQmlPropertyData *pd = resolver.property(subProperty.toString());
867 if (!pd)
868 continue;
869 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), pd->coreIndex());
870 return true;
871 }
872
873 for (auto it = targetObject->aliasesBegin(), end = targetObject->aliasesEnd();
874 it != end; ++it) {
875 auto innerAlias = *it;
876 if (compiler->stringAt(innerAlias.nameIndex()) != property)
877 continue;
878 const int innerObjectIndex = idToObjectIndex.value(innerAlias.idIndex(), -1);
879 if (innerObjectIndex == -1)
880 continue;
881 const auto &cache = propertyCaches->at(innerObjectIndex);
882 if (!cache)
883 continue;
884 QQmlPropertyResolver resolver(cache);
885 const QQmlPropertyData *pd = resolver.property(subProperty.toString());
886 if (!pd)
887 continue;
888 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), pd->coreIndex());
889 return true;
890 }
891
892 const QQmlPropertyCache::ConstPtr typeCache
893 = QQmlMetaType::propertyCacheForType(targetPropertyType);
894 if (typeCache) {
895 const QQmlPropertyResolver resolver(typeCache);
896 const QQmlPropertyData *pd = resolver.property(subProperty.toString());
897 if (pd) {
898 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), pd->coreIndex());
899 return true;
900 }
901 }
902
903 return false;
904}
905
906template<>
907typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
908QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
909 const CompiledObject &component, int objectIndex,
910 QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> *aliasCacheCreator, QQmlError *error)
911{
912 Q_UNUSED(component);
913
914 const QmlIR::Object *obj = m_compiler->objectAt(objectIndex);
915 if (!obj->aliasCount())
916 return AllAliasesResolved;
917
918 int aliasIndex = 0;
919 int numSkippedAliases = 0;
920 bool hasUnresolvedLocalAliases = false;
921
922 for (const QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) {
923 if (resolvedAliases.contains(alias)) {
924 ++numSkippedAliases;
925 continue;
926 }
927
928
929 const int idIndex = alias->idIndex();
930 const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1);
931 if (targetObjectIndex == -1) {
932 *error = qQmlCompileError(
933 alias->referenceLocation(),
934 QQmlComponentAndAliasResolverBase::tr(
935 "Invalid alias reference. Unable to find id \"%1\"")
936 .arg(stringAt(idIndex)));
937 break;
938 }
939
940 const QmlIR::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
941 Q_ASSERT(targetObject->id >= 0);
942 const int resolvedTargetObjectId = targetObject->id;
943
944 const QString aliasPropertyValue = stringAt(alias->propertyNameIndex());
945
946 QStringView property;
947 QStringView subProperty;
948
949 const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
950 if (propertySeparator != -1) {
951 property = QStringView{aliasPropertyValue}.left(propertySeparator);
952 subProperty = QStringView{aliasPropertyValue}.mid(propertySeparator + 1);
953 } else
954 property = QStringView(aliasPropertyValue);
955
956 QQmlPropertyIndex propIdx;
957
958 if (property.isEmpty()) {
959 // Alias points to object. No need to resolve properties.
960 } else {
961 QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
962 if (!targetCache) {
963 *error = qQmlCompileError(
964 alias->referenceLocation(),
965 QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1")
966 .arg(property.toString()));
967 break;
968 }
969
970 QQmlPropertyResolver resolver(targetCache);
971
972 const QQmlPropertyData *targetProperty = resolver.property(
973 property.toString(), nullptr, QQmlPropertyResolver::IgnoreRevision);
974
975 // If it's an alias that we haven't resolved yet, try again later.
976 if (!targetProperty) {
977 bool aliasPointsToOtherAlias = false;
978 int localAliasIndex = 0;
979 auto targetAlias = targetObject->aliasesBegin();
980 for (const auto end = targetObject->aliasesEnd(); targetAlias != end;
981 ++targetAlias, ++localAliasIndex) {
982 if (stringAt(targetAlias->nameIndex()) == property) {
983 aliasPointsToOtherAlias = true;
984 break;
985 }
986 }
987 if (aliasPointsToOtherAlias) {
988 if (targetObjectIndex != objectIndex) {
989 // Don't continue, yet. We need to respect the order of objects.
990 return aliasIndex == numSkippedAliases
991 ? NoAliasResolved
992 : SomeAliasesResolved;
993 }
994
995 if (resolvedAliases.contains(targetAlias)) {
996 // Target already resolved. We can set the alias right away.
997 if (!appendAliasToPropertyCache(
998 &component, alias, objectIndex, aliasIndex, -1,
999 resolvedTargetObjectId, aliasCacheCreator, error)) {
1000 break;
1001 }
1002 continue;
1003 }
1004
1005 // Target isn't resolved yet, but it's in the same object.
1006 // Continue with the other aliases.
1007 // Try again later and resolve the target alias first.
1008 ++numSkippedAliases;
1009 hasUnresolvedLocalAliases = true;
1010 continue;
1011 }
1012 }
1013
1014 if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
1015 *error = qQmlCompileError(
1016 alias->referenceLocation(),
1017 QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1")
1018 .arg(property.toString()));
1019 break;
1020 }
1021
1022 propIdx = QQmlPropertyIndex(targetProperty->coreIndex());
1023
1024 if (!subProperty.isEmpty()) {
1025 const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(targetProperty->propType());
1026 if (!valueTypeMetaObject) {
1027 // could be a deep alias
1028 bool isDeepAlias = subProperty.at(0).isLower();
1029 if (isDeepAlias) {
1030 isDeepAlias = resolveDeepAlias(
1031 m_compiler, targetObject, property, subProperty, propIdx,
1032 targetProperty->propType(), m_propertyCaches, m_idToObjectIndex);
1033 }
1034 if (!isDeepAlias) {
1035 *error = qQmlCompileError(
1036 alias->referenceLocation(),
1037 QQmlComponentAndAliasResolverBase::tr(
1038 "Invalid alias target location: %1")
1039 .arg(subProperty.toString()));
1040 break;
1041 }
1042 } else {
1043
1044 int valueTypeIndex =
1045 valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
1046 if (valueTypeIndex == -1) {
1047 *error = qQmlCompileError(
1048 alias->referenceLocation(),
1049 QQmlComponentAndAliasResolverBase::tr(
1050 "Invalid alias target location: %1")
1051 .arg(subProperty.toString()));
1052 break;
1053 }
1054 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
1055
1056 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
1057 }
1058 }
1059 }
1060
1061 if (!appendAliasToPropertyCache(
1062 &component, alias, objectIndex, aliasIndex, propIdx.toEncoded(),
1063 resolvedTargetObjectId, aliasCacheCreator, error)) {
1064 break;
1065 }
1066 }
1067
1068 if (numSkippedAliases == aliasIndex)
1069 return NoAliasResolved;
1070
1071 if (aliasIndex == obj->aliasCount() && !hasUnresolvedLocalAliases)
1072 return AllAliasesResolved;
1073
1074 return SomeAliasesResolved;
1075}
1076
1077QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
1078 : QQmlCompilePass(typeCompiler)
1079 , qmlObjects(typeCompiler->qmlObjects())
1080 , propertyCaches(typeCompiler->propertyCaches())
1081 , customParsers(typeCompiler->customParserCache())
1082 , _seenObjectWithId(false)
1083{
1084}
1085
1087{
1088 for (int i = 0; i < qmlObjects->size(); ++i) {
1089 if ((qmlObjects->at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
1090 && !scanObject(i, ScopeDeferred::False)) {
1091 return false;
1092 }
1093 }
1094 return scanObject(/*root object*/0, ScopeDeferred::False);
1095}
1096
1098 int objectIndex, ScopeDeferred scopeDeferred)
1099{
1100 using namespace QV4::CompiledData;
1101
1102 QmlIR::Object *obj = qmlObjects->at(objectIndex);
1103 if (obj->idNameIndex != 0)
1104 _seenObjectWithId = true;
1105
1106 if (obj->flags & Object::IsComponent) {
1107 Q_ASSERT(obj->bindingCount() == 1);
1108 const Binding *componentBinding = obj->firstBinding();
1109 Q_ASSERT(componentBinding->type() == Binding::Type_Object);
1110 // Components are separate from their surrounding scope. They cannot be deferred.
1111 return scanObject(componentBinding->value.objectIndex, ScopeDeferred::False);
1112 }
1113
1114 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(objectIndex);
1115 if (!propertyCache)
1116 return true;
1117
1118 QString defaultPropertyName;
1119 const QQmlPropertyData *defaultProperty = nullptr;
1120 if (obj->indexOfDefaultPropertyOrAlias != -1) {
1121 const QQmlPropertyCache *cache = propertyCache->parent().data();
1122 defaultPropertyName = cache->defaultPropertyName();
1123 defaultProperty = cache->defaultProperty();
1124 } else {
1125 defaultPropertyName = propertyCache->defaultPropertyName();
1126 defaultProperty = propertyCache->defaultProperty();
1127 }
1128
1129 QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
1130
1131 QQmlPropertyResolver propertyResolver(propertyCache);
1132
1133 QStringList deferredPropertyNames;
1134 QStringList immediatePropertyNames;
1135 {
1136 const QMetaObject *mo = propertyCache->firstCppMetaObject();
1137 const int deferredNamesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
1138 const int immediateNamesIndex = mo->indexOfClassInfo("ImmediatePropertyNames");
1139 if (deferredNamesIndex != -1) {
1140 if (immediateNamesIndex != -1) {
1141 COMPILE_EXCEPTION(obj, tr("You cannot define both DeferredPropertyNames and "
1142 "ImmediatePropertyNames on the same type."));
1143 }
1144 const QMetaClassInfo classInfo = mo->classInfo(deferredNamesIndex);
1145 deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(u',');
1146 } else if (immediateNamesIndex != -1) {
1147 const QMetaClassInfo classInfo = mo->classInfo(immediateNamesIndex);
1148 immediatePropertyNames = QString::fromUtf8(classInfo.value()).split(u',');
1149
1150 // If the property contains an empty string, all properties shall be deferred.
1151 if (immediatePropertyNames.isEmpty())
1152 immediatePropertyNames.append(QString());
1153 }
1154 }
1155
1156 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
1157 QString name = stringAt(binding->propertyNameIndex);
1158
1159 if (customParser) {
1160 if (binding->type() == Binding::Type_AttachedProperty) {
1161 if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
1162 binding->setFlag(Binding::IsCustomParserBinding);
1163 obj->flags |= Object::HasCustomParserBindings;
1164 continue;
1165 }
1166 } else if (QQmlSignalNames::isHandlerName(name)
1167 && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
1168 obj->flags |= Object::HasCustomParserBindings;
1169 binding->setFlag(Binding::IsCustomParserBinding);
1170 continue;
1171 }
1172 }
1173
1174 const bool hasPropertyData = [&]() {
1175 if (name.isEmpty()) {
1176 name = defaultPropertyName;
1177 if (defaultProperty)
1178 return true;
1179 } else if (name.constData()->isUpper()) {
1180 // Upper case names cannot be custom-parsed unless they are attached properties
1181 // and the custom parser explicitly accepts them. See above for that case.
1182 return false;
1183 } else {
1184 bool notInRevision = false;
1185 if (propertyResolver.property(
1186 name, &notInRevision, QQmlPropertyResolver::CheckRevision)) {
1187 return true;
1188 }
1189 }
1190
1191 if (!customParser)
1192 return false;
1193
1194 const Binding::Flags bindingFlags = binding->flags();
1195 if (bindingFlags & Binding::IsSignalHandlerExpression
1196 || bindingFlags & Binding::IsSignalHandlerObject
1197 || bindingFlags & Binding::IsPropertyObserver) {
1198 // These signal handlers cannot be custom-parsed. We have already established
1199 // that the signal exists.
1200 return false;
1201 }
1202
1203 // If the property isn't found, we may want to custom-parse the binding.
1204 obj->flags |= Object::HasCustomParserBindings;
1205 binding->setFlag(Binding::IsCustomParserBinding);
1206 return false;
1207 }();
1208
1209 bool seenSubObjectWithId = false;
1210 bool isExternal = false;
1211 if (binding->type() >= Binding::Type_Object) {
1212 const bool isOwnProperty = hasPropertyData || binding->isAttachedProperty();
1213 isExternal = !isOwnProperty && binding->isGroupProperty();
1214 if (isOwnProperty || isExternal) {
1215 qSwap(_seenObjectWithId, seenSubObjectWithId);
1216 const bool subObjectValid = scanObject(
1217 binding->value.objectIndex,
1218 (isExternal || scopeDeferred == ScopeDeferred::True)
1219 ? ScopeDeferred::True
1220 : ScopeDeferred::False);
1221 qSwap(_seenObjectWithId, seenSubObjectWithId);
1222 if (!subObjectValid)
1223 return false;
1224 _seenObjectWithId |= seenSubObjectWithId;
1225 }
1226 }
1227
1228 bool isDeferred = false;
1229 if (!immediatePropertyNames.isEmpty() && !immediatePropertyNames.contains(name)) {
1230 if (seenSubObjectWithId) {
1231 COMPILE_EXCEPTION(binding, tr("You cannot assign an id to an object assigned "
1232 "to a deferred property."));
1233 }
1234 if (isExternal || !disableInternalDeferredProperties())
1235 isDeferred = true;
1236 } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
1237 if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty) {
1238 if (isExternal || !disableInternalDeferredProperties())
1239 isDeferred = true;
1240 }
1241 }
1242
1243 if (binding->type() >= Binding::Type_Object) {
1244 if (isExternal && !isDeferred && !customParser) {
1246 binding, tr("Cannot assign to non-existent property \"%1\"").arg(name));
1247 }
1248 }
1249
1250 if (isDeferred) {
1251 binding->setFlag(Binding::IsDeferredBinding);
1252 obj->flags |= Object::HasDeferredBindings;
1253 }
1254 }
1255
1256 return true;
1257}
1258
1266
1268{
1269 for (int i = 0; i < qmlObjects.size(); ++i)
1270 mergeDefaultProperties(i);
1271}
1272
1273void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
1274{
1275 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(objectIndex);
1276 if (!propertyCache)
1277 return;
1278
1279 QmlIR::Object *object = qmlObjects.at(objectIndex);
1280
1281 QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
1282 QmlIR::Binding *bindingsToReinsert = nullptr;
1283 QmlIR::Binding *tail = nullptr;
1284
1285 QmlIR::Binding *previousBinding = nullptr;
1286 QmlIR::Binding *binding = object->firstBinding();
1287 while (binding) {
1288 if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) {
1289 previousBinding = binding;
1290 binding = binding->next;
1291 continue;
1292 }
1293
1294 QmlIR::Binding *toReinsert = binding;
1295 binding = object->unlinkBinding(previousBinding, binding);
1296
1297 if (!tail) {
1298 bindingsToReinsert = toReinsert;
1299 tail = toReinsert;
1300 } else {
1301 tail->next = toReinsert;
1302 tail = tail->next;
1303 }
1304 tail->next = nullptr;
1305 }
1306
1307 binding = bindingsToReinsert;
1308 while (binding) {
1309 QmlIR::Binding *toReinsert = binding;
1310 binding = binding->next;
1311 object->insertSorted(toReinsert);
1312 }
1313}
1314
1315QT_END_NAMESPACE
QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler)
QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler)
QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler)
Combined button and popup list for selecting options.
Definition qjsvalue.h:24
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define COMPILE_EXCEPTION(location, desc)
static bool resolveDeepAlias(QQmlTypeCompiler *compiler, const QmlIR::Object *targetObject, QStringView property, QStringView subProperty, QQmlPropertyIndex &propIdx, QMetaType targetPropertyType, const QQmlPropertyCacheVector *propertyCaches, const QMap< int, int > &idToObjectIndex)
QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
QQmlTypeCompiler * compiler
int registerConstant(QV4::ReturnedValue v)
void recordError(const QQmlJS::DiagnosticMessage &message)
const QV4::CompiledData::Unit * qmlUnit() const
QQmlType qmlTypeForComponent(const QString &inlineComponentName=QString()) const
QList< QmlIR::Object * > * qmlObjects() const
QQmlPropertyCacheVector * propertyCaches()
QQmlJS::MemoryPool * memoryPool()
void recordError(const QQmlError &e)
QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const
const QQmlPropertyCacheVector * propertyCaches() const
int registerString(const QString &str)
const QV4::Compiler::StringTableGenerator * stringPool() const
void recordError(const QV4::CompiledData::Location &location, const QString &description)
QStringView newStringRef(const QString &string)
void addImport(const QString &module, const QString &qualifier, QTypeRevision version)
QQmlRefPointer< QV4::CompiledData::CompilationUnit > compile()
QString stringAt(int idx) const
const QQmlImports * imports() const