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

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