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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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