1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmltypecompiler_p.h"
41
42#include <private/qqmlobjectcreator_p.h>
43#include <private/qqmlcustomparser_p.h>
44#include <private/qqmlvmemetaobject_p.h>
45#include <private/qqmlcomponent_p.h>
46#include <private/qqmlpropertyresolver_p.h>
47
48#define COMPILE_EXCEPTION(token, desc) \
49 { \
50 recordError((token)->location, desc); \
51 return false; \
52 }
53
54QT_BEGIN_NAMESPACE
55
56QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
57 QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
58 QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
59 : resolvedTypes(resolvedTypeCache)
60 , engine(engine)
61 , dependencyHasher(dependencyHasher)
62 , document(parsedQML)
63 , typeNameCache(typeNameCache)
64 , typeData(typeData)
65{
66}
67
68QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
69{
70 // Build property caches and VME meta object data
71
72 for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd();
73 it != end; ++it) {
74 QQmlCustomParser *customParser = (*it)->type.customParser();
75 if (customParser)
76 customParsers.insert(akey: it.key(), avalue: customParser);
77 }
78
79 QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
80
81
82 {
83 QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings,
84 engine, this, imports(), typeData->typeClassName());
85 QQmlError error = propertyCacheBuilder.buildMetaObjects();
86 if (error.isValid()) {
87 recordError(e: error);
88 return nullptr;
89 }
90 }
91
92 {
93 QQmlDefaultPropertyMerger merger(this);
94 merger.mergeDefaultProperties();
95 }
96
97 {
98 SignalHandlerConverter converter(this);
99 if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations())
100 return nullptr;
101 }
102
103 {
104 QQmlEnumTypeResolver enumResolver(this);
105 if (!enumResolver.resolveEnumBindings())
106 return nullptr;
107 }
108
109 {
110 QQmlCustomParserScriptIndexer cpi(this);
111 cpi.annotateBindingsWithScriptStrings();
112 }
113
114 {
115 QQmlAliasAnnotator annotator(this);
116 annotator.annotateBindingsToAliases();
117 }
118
119 // Resolve component boundaries and aliases
120
121 {
122 // Scan for components, determine their scopes and resolve aliases within the scope.
123 QQmlComponentAndAliasResolver resolver(this);
124 if (!resolver.resolve())
125 return nullptr;
126
127 pendingGroupPropertyBindings.resolveMissingPropertyCaches(enginePrivate: engine, propertyCaches: &m_propertyCaches);
128 }
129
130 {
131 QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this);
132 if (!deferredAndCustomParserBindingScanner.scanObject())
133 return nullptr;
134 }
135
136 if (!document->javaScriptCompilationUnit.unitData()) {
137 // Compile JS binding expressions and signal handlers if necessary
138 {
139 // We can compile script strings ahead of time, but they must be compiled
140 // without type optimizations as their scope is always entirely dynamic.
141 QQmlScriptStringScanner sss(this);
142 sss.scan();
143 }
144
145 document->jsModule.fileName = typeData->urlString();
146 document->jsModule.finalUrl = typeData->finalUrlString();
147 QmlIR::JSCodeGen v4CodeGenerator(document, engine->v4engine()->illegalNames());
148 if (!v4CodeGenerator.generateCodeForComponents(componentRoots: componentRoots())) {
149 recordError(message: v4CodeGenerator.error());
150 return nullptr;
151 }
152
153 document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/generateUnitData: false);
154 }
155
156 // Generate QML compiled type data structures
157
158 QmlIR::QmlUnitGenerator qmlGenerator;
159 qmlGenerator.generate(output&: *document, dependencyHasher);
160
161 if (!errors.isEmpty())
162 return nullptr;
163
164 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
165 = QV4::ExecutableCompilationUnit::create(compilationUnit: std::move(
166 document->javaScriptCompilationUnit));
167 compilationUnit->typeNameCache = typeNameCache;
168 compilationUnit->resolvedTypes = *resolvedTypes;
169 compilationUnit->propertyCaches = std::move(m_propertyCaches);
170 Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount()));
171 return compilationUnit;
172}
173
174void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
175{
176 QQmlError error;
177 error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: location.line));
178 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: location.column));
179 error.setDescription(description);
180 error.setUrl(url());
181 errors << error;
182}
183
184void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message)
185{
186 QQmlError error;
187 error.setDescription(message.message);
188 error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: message.loc.startLine));
189 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: message.loc.startColumn));
190 error.setUrl(url());
191 errors << error;
192}
193
194void QQmlTypeCompiler::recordError(const QQmlError &e)
195{
196 QQmlError error = e;
197 error.setUrl(url());
198 errors << error;
199}
200
201QString QQmlTypeCompiler::stringAt(int idx) const
202{
203 return document->stringAt(index: idx);
204}
205
206int QQmlTypeCompiler::registerString(const QString &str)
207{
208 return document->jsGenerator.registerString(str);
209}
210
211int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
212{
213 return document->jsGenerator.registerConstant(v);
214}
215
216const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
217{
218 return document->javaScriptCompilationUnit.unitData();
219}
220
221const QQmlImports *QQmlTypeCompiler::imports() const
222{
223 return &typeData->imports();
224}
225
226QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
227{
228 return &document->objects;
229}
230
231void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
232{
233 m_propertyCaches = std::move(caches);
234 Q_ASSERT(m_propertyCaches.count() > 0);
235}
236
237const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
238{
239 return &m_propertyCaches;
240}
241
242QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
243{
244 return std::move(m_propertyCaches);
245}
246
247QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
248{
249 return document->jsParserEngine.pool();
250}
251
252QStringRef QQmlTypeCompiler::newStringRef(const QString &string)
253{
254 return document->jsParserEngine.newStringRef(s: string);
255}
256
257const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const
258{
259 return &document->jsGenerator.stringTable;
260}
261
262QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
263{
264 return object->bindingAsString(doc: document, scriptIndex);
265}
266
267void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion)
268{
269 const quint32 moduleIdx = registerString(str: module);
270 const quint32 qualifierIdx = registerString(str: qualifier);
271
272 for (int i = 0, count = document->imports.count(); i < count; ++i) {
273 const QV4::CompiledData::Import *existingImport = document->imports.at(i);
274 if (existingImport->type == QV4::CompiledData::Import::ImportLibrary
275 && existingImport->uriIndex == moduleIdx
276 && existingImport->qualifierIndex == qualifierIdx)
277 return;
278 }
279 auto pool = memoryPool();
280 QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
281 import->type = QV4::CompiledData::Import::ImportLibrary;
282 import->majorVersion = majorVersion;
283 import->minorVersion = minorVersion;
284 import->uriIndex = moduleIdx;
285 import->qualifierIndex = qualifierIdx;
286 document->imports.append(t: import);
287}
288
289CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const
290{
291 return typeData->typeIds(objectId);
292}
293
294QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
295 : compiler(typeCompiler)
296{
297}
298
299
300
301SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
302 : QQmlCompilePass(typeCompiler)
303 , enginePrivate(typeCompiler->enginePrivate())
304 , qmlObjects(*typeCompiler->qmlObjects())
305 , imports(typeCompiler->imports())
306 , customParsers(typeCompiler->customParserCache())
307 , illegalNames(typeCompiler->enginePrivate()->v4engine()->illegalNames())
308 , propertyCaches(typeCompiler->propertyCaches())
309{
310}
311
312bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations()
313{
314 for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) {
315 const QmlIR::Object * const obj = qmlObjects.at(i: objectIndex);
316 QQmlPropertyCache *cache = propertyCaches->at(index: objectIndex);
317 if (!cache)
318 continue;
319 if (QQmlCustomParser *customParser = customParsers.value(akey: obj->inheritedTypeNameIndex)) {
320 if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers))
321 continue;
322 }
323 const QString elementName = stringAt(idx: obj->inheritedTypeNameIndex);
324 if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, typeName: elementName, propertyCache: cache))
325 return false;
326 }
327 return true;
328}
329
330bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache)
331{
332 // map from signal name defined in qml itself to list of parameters
333 QHash<QString, QStringList> customSignals;
334
335 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
336 QString propertyName = stringAt(idx: binding->propertyNameIndex);
337 // Attached property?
338 if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
339 const QmlIR::Object *attachedObj = qmlObjects.at(i: binding->value.objectIndex);
340 auto *typeRef = resolvedType(id: binding->propertyNameIndex);
341 QQmlType type = typeRef ? typeRef->type : QQmlType();
342 if (!type.isValid())
343 imports->resolveType(type: propertyName, type_return: &type, version_major: nullptr, version_minor: nullptr, ns_return: nullptr);
344
345 const QMetaObject *attachedType = type.attachedPropertiesType(engine: enginePrivate);
346 if (!attachedType)
347 COMPILE_EXCEPTION(binding, tr("Non-existent attached object"));
348 QQmlPropertyCache *cache = compiler->enginePrivate()->cache(metaObject: attachedType);
349 if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj: attachedObj, typeName: propertyName, propertyCache: cache))
350 return false;
351 continue;
352 }
353
354 if (!QmlIR::IRBuilder::isSignalPropertyName(name: propertyName))
355 continue;
356
357 QQmlPropertyResolver resolver(propertyCache);
358
359 Q_ASSERT(propertyName.startsWith(QLatin1String("on")));
360 propertyName.remove(i: 0, len: 2);
361
362 // Note that the property name could start with any alpha or '_' or '$' character,
363 // so we need to do the lower-casing of the first alpha character.
364 for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) {
365 if (propertyName.at(i: firstAlphaIndex).isUpper()) {
366 propertyName[firstAlphaIndex] = propertyName.at(i: firstAlphaIndex).toLower();
367 break;
368 }
369 }
370
371 QList<QString> parameters;
372
373 bool notInRevision = false;
374 QQmlPropertyData *signal = resolver.signal(name: propertyName, notInRevision: &notInRevision);
375 if (signal) {
376 int sigIndex = propertyCache->methodIndexToSignalIndex(index: signal->coreIndex());
377 sigIndex = propertyCache->originalClone(index: sigIndex);
378
379 bool unnamedParameter = false;
380
381 QList<QByteArray> parameterNames = propertyCache->signalParameterNames(index: sigIndex);
382 for (int i = 0; i < parameterNames.count(); ++i) {
383 const QString param = QString::fromUtf8(str: parameterNames.at(i));
384 if (param.isEmpty())
385 unnamedParameter = true;
386 else if (unnamedParameter) {
387 COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter."));
388 } else if (illegalNames.contains(value: param)) {
389 COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param));
390 }
391 parameters += param;
392 }
393 } else {
394 if (notInRevision) {
395 // Try assinging it as a property later
396 if (resolver.property(name: propertyName, /*notInRevision ptr*/notInRevision: nullptr))
397 continue;
398
399 const QString &originalPropertyName = stringAt(idx: binding->propertyNameIndex);
400
401 auto *typeRef = resolvedType(id: obj->inheritedTypeNameIndex);
402 const QQmlType type = typeRef ? typeRef->type : QQmlType();
403 if (type.isValid()) {
404 COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion()));
405 } else {
406 COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName));
407 }
408 }
409
410 // Try to look up the signal parameter names in the object itself
411
412 // build cache if necessary
413 if (customSignals.isEmpty()) {
414 for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) {
415 const QString &signalName = stringAt(idx: signal->nameIndex);
416 customSignals.insert(akey: signalName, avalue: signal->parameterStringList(stringPool: compiler->stringPool()));
417 }
418
419 for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) {
420 const QString propName = stringAt(idx: property->nameIndex);
421 customSignals.insert(akey: propName, avalue: QStringList());
422 }
423 }
424
425 QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(akey: propertyName);
426 if (entry == customSignals.constEnd() && propertyName.endsWith(s: QLatin1String("Changed"))) {
427 QString alternateName = propertyName.mid(position: 0, n: propertyName.length() - static_cast<int>(strlen(s: "Changed")));
428 entry = customSignals.constFind(akey: alternateName);
429 }
430
431 if (entry == customSignals.constEnd()) {
432 // Can't find even a custom signal, then just don't do anything and try
433 // keeping the binding as a regular property assignment.
434 continue;
435 }
436
437 parameters = entry.value();
438 }
439
440 // Binding object to signal means connect the signal to the object's default method.
441 if (binding->type == QV4::CompiledData::Binding::Type_Object) {
442 binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject;
443 continue;
444 }
445
446 if (binding->type != QV4::CompiledData::Binding::Type_Script) {
447 if (binding->type < QV4::CompiledData::Binding::Type_Script) {
448 COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)"));
449 } else {
450 COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment"));
451 }
452 }
453
454 QQmlJS::MemoryPool *pool = compiler->memoryPool();
455
456 QQmlJS::AST::FormalParameterList *paramList = nullptr;
457 for (const QString &param : qAsConst(t&: parameters)) {
458 QStringRef paramNameRef = compiler->newStringRef(string: param);
459
460 QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr);
461 paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b);
462 }
463
464 if (paramList)
465 paramList = paramList->finish(pool);
466
467 QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(index: binding->value.compiledScriptIndex);
468 QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr;
469 if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(ast: foe->node)) {
470 if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(ast: es->expression)) {
471 functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body);
472 functionDeclaration->functionToken = fe->functionToken;
473 functionDeclaration->identifierToken = fe->identifierToken;
474 functionDeclaration->lparenToken = fe->lparenToken;
475 functionDeclaration->rparenToken = fe->rparenToken;
476 functionDeclaration->lbraceToken = fe->lbraceToken;
477 functionDeclaration->rbraceToken = fe->rbraceToken;
478 }
479 }
480 if (!functionDeclaration) {
481 QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
482 QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement);
483 body = body->finish();
484
485 functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(string: stringAt(idx: binding->propertyNameIndex)), paramList, body);
486 functionDeclaration->lbraceToken = functionDeclaration->functionToken
487 = foe->node->firstSourceLocation();
488 functionDeclaration->rbraceToken = foe->node->lastSourceLocation();
489 }
490 foe->node = functionDeclaration;
491 binding->propertyNameIndex = compiler->registerString(str: propertyName);
492 binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression;
493 }
494 return true;
495}
496
497QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
498 : QQmlCompilePass(typeCompiler)
499 , qmlObjects(*typeCompiler->qmlObjects())
500 , propertyCaches(typeCompiler->propertyCaches())
501 , imports(typeCompiler->imports())
502{
503}
504
505bool QQmlEnumTypeResolver::resolveEnumBindings()
506{
507 for (int i = 0; i < qmlObjects.count(); ++i) {
508 QQmlPropertyCache *propertyCache = propertyCaches->at(index: i);
509 if (!propertyCache)
510 continue;
511 const QmlIR::Object *obj = qmlObjects.at(i);
512
513 QQmlPropertyResolver resolver(propertyCache);
514
515 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
516 if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
517 || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
518 continue;
519
520 if (binding->type != QV4::CompiledData::Binding::Type_Script)
521 continue;
522
523 const QString propertyName = stringAt(idx: binding->propertyNameIndex);
524 bool notInRevision = false;
525 QQmlPropertyData *pd = resolver.property(name: propertyName, notInRevision: &notInRevision);
526 if (!pd)
527 continue;
528
529 if (!pd->isEnum() && pd->propType() != QMetaType::Int)
530 continue;
531
532 if (!tryQualifiedEnumAssignment(obj, propertyCache, prop: pd, binding))
533 return false;
534 }
535 }
536
537 return true;
538}
539
540struct StaticQtMetaObject : public QObject
541{
542 static const QMetaObject *get()
543 { return &staticQtMetaObject; }
544};
545
546bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject)
547{
548 if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) {
549 COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString()));
550 }
551 binding->type = QV4::CompiledData::Binding::Type_Number;
552 binding->value.constantValueIndex = compiler->registerConstant(v: QV4::Encode((double)enumValue));
553// binding->setNumberValueInternal((double)enumValue);
554 binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
555 return true;
556}
557
558bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding)
559{
560 bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum();
561 if (!prop->isEnum() && !isIntProp)
562 return true;
563
564 if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))
565 COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex)));
566
567 Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
568 const QString string = compiler->bindingAsString(object: obj, scriptIndex: binding->value.compiledScriptIndex);
569 if (!string.constData()->isUpper())
570 return true;
571
572 // we support one or two '.' in the enum phrase:
573 // * <TypeName>.<EnumValue>
574 // * <TypeName>.<ScopedEnumName>.<EnumValue>
575
576 int dot = string.indexOf(c: QLatin1Char('.'));
577 if (dot == -1 || dot == string.length()-1)
578 return true;
579
580 int dot2 = string.indexOf(c: QLatin1Char('.'), from: dot+1);
581 if (dot2 != -1 && dot2 != string.length()-1) {
582 if (!string.at(i: dot+1).isUpper())
583 return true;
584 if (string.indexOf(c: QLatin1Char('.'), from: dot2+1) != -1)
585 return true;
586 }
587
588 QHashedStringRef typeName(string.constData(), dot);
589 const bool isQtObject = (typeName == QLatin1String("Qt"));
590 const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(position: dot + 1, n: dot2 - dot - 1) : QStringRef());
591 // ### consider supporting scoped enums in Qt namespace
592 const QStringRef enumValue = string.midRef(position: !isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1);
593
594 if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here?
595 // Allow enum assignment to ints.
596 bool ok;
597 int enumval = evaluateEnum(scope: typeName.toString(), enumName: scopedEnumName, enumValue, ok: &ok);
598 if (ok) {
599 if (!assignEnumToBinding(binding, enumName: enumValue, enumValue: enumval, isQtObject))
600 return false;
601 }
602 return true;
603 }
604 QQmlType type;
605 imports->resolveType(type: typeName, type_return: &type, version_major: nullptr, version_minor: nullptr, ns_return: nullptr);
606
607 if (!type.isValid() && !isQtObject)
608 return true;
609
610 int value = 0;
611 bool ok = false;
612
613 auto *tr = resolvedType(id: obj->inheritedTypeNameIndex);
614 if (type.isValid() && tr && tr->type == type) {
615 // When these two match, we can short cut the search
616 QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(index: prop->coreIndex());
617 QMetaEnum menum = mprop.enumerator();
618 QByteArray enumName = enumValue.toUtf8();
619 if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8())
620 return true;
621
622 if (mprop.isFlagType()) {
623 value = menum.keysToValue(keys: enumName.constData(), ok: &ok);
624 } else {
625 value = menum.keyToValue(key: enumName.constData(), ok: &ok);
626 }
627 } else {
628 // Otherwise we have to search the whole type
629 if (type.isValid()) {
630 if (!scopedEnumName.isEmpty())
631 value = type.scopedEnumValue(engine: compiler->enginePrivate(), scopedEnumName, enumValue, ok: &ok);
632 else
633 value = type.enumValue(engine: compiler->enginePrivate(), QHashedStringRef(enumValue), ok: &ok);
634 } else {
635 QByteArray enumName = enumValue.toUtf8();
636 const QMetaObject *metaObject = StaticQtMetaObject::get();
637 for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
638 QMetaEnum e = metaObject->enumerator(index: ii);
639 value = e.keyToValue(key: enumName.constData(), ok: &ok);
640 }
641 }
642 }
643
644 if (!ok)
645 return true;
646
647 return assignEnumToBinding(binding, enumName: enumValue, enumValue: value, isQtObject);
648}
649
650int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const
651{
652 Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer");
653 *ok = false;
654
655 if (scope != QLatin1String("Qt")) {
656 QQmlType type;
657 imports->resolveType(type: scope, type_return: &type, version_major: nullptr, version_minor: nullptr, ns_return: nullptr);
658 if (!type.isValid())
659 return -1;
660 if (!enumName.isEmpty())
661 return type.scopedEnumValue(engine: compiler->enginePrivate(), enumName, enumValue, ok);
662 return type.enumValue(engine: compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok);
663 }
664
665 const QMetaObject *mo = StaticQtMetaObject::get();
666 int i = mo->enumeratorCount();
667 const QByteArray ba = enumValue.toUtf8();
668 while (i--) {
669 int v = mo->enumerator(index: i).keyToValue(key: ba.constData(), ok);
670 if (*ok)
671 return v;
672 }
673 return -1;
674}
675
676QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler)
677 : QQmlCompilePass(typeCompiler)
678 , qmlObjects(*typeCompiler->qmlObjects())
679 , customParsers(typeCompiler->customParserCache())
680{
681}
682
683void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings()
684{
685 scanObjectRecursively(/*root object*/objectIndex: 0);
686 for (int i = 0; i < qmlObjects.size(); ++i)
687 if (qmlObjects.at(i)->isInlineComponent)
688 scanObjectRecursively(objectIndex: i);
689}
690
691void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings)
692{
693 const QmlIR::Object * const obj = qmlObjects.at(i: objectIndex);
694 if (!annotateScriptBindings)
695 annotateScriptBindings = customParsers.contains(akey: obj->inheritedTypeNameIndex);
696 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
697 if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
698 scanObjectRecursively(objectIndex: binding->value.objectIndex, annotateScriptBindings);
699 continue;
700 } else if (binding->type != QV4::CompiledData::Binding::Type_Script)
701 continue;
702 if (!annotateScriptBindings)
703 continue;
704 const QString script = compiler->bindingAsString(object: obj, scriptIndex: binding->value.compiledScriptIndex);
705 binding->stringIndex = compiler->registerString(str: script);
706 }
707}
708
709QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
710 : QQmlCompilePass(typeCompiler)
711 , qmlObjects(*typeCompiler->qmlObjects())
712 , propertyCaches(typeCompiler->propertyCaches())
713{
714}
715
716void QQmlAliasAnnotator::annotateBindingsToAliases()
717{
718 for (int i = 0; i < qmlObjects.count(); ++i) {
719 QQmlPropertyCache *propertyCache = propertyCaches->at(index: i);
720 if (!propertyCache)
721 continue;
722
723 const QmlIR::Object *obj = qmlObjects.at(i);
724
725 QQmlPropertyResolver resolver(propertyCache);
726 QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
727
728 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
729 if (!binding->isValueBinding())
730 continue;
731 bool notInRevision = false;
732 QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(name: stringAt(idx: binding->propertyNameIndex), notInRevision: &notInRevision) : defaultProperty;
733 if (pd && pd->isAlias())
734 binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
735 }
736 }
737}
738
739QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler)
740 : QQmlCompilePass(typeCompiler)
741 , qmlObjects(*typeCompiler->qmlObjects())
742 , propertyCaches(typeCompiler->propertyCaches())
743{
744
745}
746
747void QQmlScriptStringScanner::scan()
748{
749 const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>();
750 for (int i = 0; i < qmlObjects.count(); ++i) {
751 QQmlPropertyCache *propertyCache = propertyCaches->at(index: i);
752 if (!propertyCache)
753 continue;
754
755 const QmlIR::Object *obj = qmlObjects.at(i);
756
757 QQmlPropertyResolver resolver(propertyCache);
758 QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
759
760 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
761 if (binding->type != QV4::CompiledData::Binding::Type_Script)
762 continue;
763 bool notInRevision = false;
764 QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(name: stringAt(idx: binding->propertyNameIndex), notInRevision: &notInRevision) : defaultProperty;
765 if (!pd || pd->propType() != scriptStringMetaType)
766 continue;
767
768 QString script = compiler->bindingAsString(object: obj, scriptIndex: binding->value.compiledScriptIndex);
769 binding->stringIndex = compiler->registerString(str: script);
770 }
771 }
772}
773
774QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler)
775 : QQmlCompilePass(typeCompiler)
776 , enginePrivate(typeCompiler->enginePrivate())
777 , pool(typeCompiler->memoryPool())
778 , qmlObjects(typeCompiler->qmlObjects())
779 , propertyCaches(std::move(typeCompiler->takePropertyCaches()))
780{
781}
782
783static bool isUsableComponent(const QMetaObject *metaObject)
784{
785 // The metaObject is a component we're interested in if it either is a QQmlComponent itself
786 // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
787 // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
788
789 if (metaObject == &QQmlComponent::staticMetaObject)
790 return true;
791
792 for (; metaObject; metaObject = metaObject->superClass()) {
793 if (qstrcmp(str1: metaObject->className(), str2: "QQmlAbstractDelegateComponent") == 0)
794 return true;
795 }
796
797 return false;
798}
799
800void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache)
801{
802 QQmlPropertyResolver propertyResolver(propertyCache);
803
804 QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
805
806 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
807 if (binding->type != QV4::CompiledData::Binding::Type_Object)
808 continue;
809 if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
810 continue;
811
812 const QmlIR::Object *targetObject = qmlObjects->at(i: binding->value.objectIndex);
813 auto *tr = resolvedType(id: targetObject->inheritedTypeNameIndex);
814 Q_ASSERT(tr);
815
816 const QMetaObject *firstMetaObject = nullptr;
817 if (tr->type.isValid())
818 firstMetaObject = tr->type.metaObject();
819 else if (const auto compilationUnit = tr->compilationUnit())
820 firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
821 if (isUsableComponent(metaObject: firstMetaObject))
822 continue;
823 // if here, not a QQmlComponent, so needs wrapping
824
825 QQmlPropertyData *pd = nullptr;
826 if (binding->propertyNameIndex != quint32(0)) {
827 bool notInRevision = false;
828 pd = propertyResolver.property(name: stringAt(idx: binding->propertyNameIndex), notInRevision: &notInRevision);
829 } else {
830 pd = defaultProperty;
831 }
832 if (!pd || !pd->isQObject())
833 continue;
834
835 QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), minorVersion: pd->typeMinorVersion());
836 const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
837 while (mo) {
838 if (mo == &QQmlComponent::staticMetaObject)
839 break;
840 mo = mo->superClass();
841 }
842
843 if (!mo)
844 continue;
845
846 // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}"
847 QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject);
848 Q_ASSERT(componentType.isValid());
849 const QString qualifier = QStringLiteral("QmlInternals");
850
851 compiler->addImport(module: componentType.module(), qualifier, majorVersion: componentType.majorVersion(), minorVersion: componentType.minorVersion());
852
853 QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
854 syntheticComponent->init(pool, typeNameIndex: compiler->registerString(str: qualifier + QLatin1Char('.') + componentType.elementName()), idIndex: compiler->registerString(str: QString()));
855 syntheticComponent->location = binding->valueLocation;
856 syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
857
858 if (!containsResolvedType(id: syntheticComponent->inheritedTypeNameIndex)) {
859 auto typeRef = new QV4::ResolvedTypeReference;
860 typeRef->type = componentType;
861 typeRef->majorVersion = componentType.majorVersion();
862 typeRef->minorVersion = componentType.minorVersion();
863 insertResolvedType(id: syntheticComponent->inheritedTypeNameIndex, value: typeRef);
864 }
865
866 qmlObjects->append(t: syntheticComponent);
867 const int componentIndex = qmlObjects->count() - 1;
868 // Keep property caches symmetric
869 QQmlPropertyCache *componentCache = enginePrivate->cache(metaObject: &QQmlComponent::staticMetaObject);
870 propertyCaches.append(cache: componentCache);
871
872 QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
873 *syntheticBinding = *binding;
874 syntheticBinding->type = QV4::CompiledData::Binding::Type_Object;
875 QString error = syntheticComponent->appendBinding(b: syntheticBinding, /*isListBinding*/false);
876 Q_ASSERT(error.isEmpty());
877 Q_UNUSED(error);
878
879 binding->value.objectIndex = componentIndex;
880
881 componentRoots.append(t: componentIndex);
882 }
883}
884
885bool QQmlComponentAndAliasResolver::resolve()
886{
887 // Detect real Component {} objects as well as implicitly defined components, such as
888 // someItemDelegate: Item {}
889 // In the implicit case Item is surrounded by a synthetic Component {} because the property
890 // on the left hand side is of QQmlComponent type.
891 const int objCountWithoutSynthesizedComponents = qmlObjects->count();
892 for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) {
893 QmlIR::Object *obj = qmlObjects->at(i);
894 if (obj->isInlineComponent) {
895 componentRoots.append(t: i);
896 continue;
897 }
898 QQmlPropertyCache *cache = propertyCaches.at(index: i);
899 if (obj->inheritedTypeNameIndex == 0 && !cache)
900 continue;
901
902 bool isExplicitComponent = false;
903
904 if (obj->inheritedTypeNameIndex) {
905 auto *tref = resolvedType(id: obj->inheritedTypeNameIndex);
906 Q_ASSERT(tref);
907 if (tref->type.metaObject() == &QQmlComponent::staticMetaObject)
908 isExplicitComponent = true;
909 }
910 if (!isExplicitComponent) {
911 if (cache)
912 findAndRegisterImplicitComponents(obj, propertyCache: cache);
913 continue;
914 }
915
916 obj->flags |= QV4::CompiledData::Object::IsComponent;
917
918 if (obj->functionCount() > 0)
919 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
920 if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
921 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
922 if (obj->signalCount() > 0)
923 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
924
925 if (obj->bindingCount() == 0)
926 COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
927
928 const QmlIR::Binding *rootBinding = obj->firstBinding();
929
930 for (const QmlIR::Binding *b = rootBinding; b; b = b->next) {
931 if (b->propertyNameIndex != 0)
932 COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
933 }
934
935 if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
936 COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
937
938 // For the root object, we are going to collect ids/aliases and resolve them for as a separate
939 // last pass.
940 if (i != 0)
941 componentRoots.append(t: i);
942
943 }
944
945 for (int i = 0; i < componentRoots.count(); ++i) {
946 QmlIR::Object *component = qmlObjects->at(i: componentRoots.at(i));
947 const QmlIR::Binding *rootBinding = component->firstBinding();
948
949 _idToObjectIndex.clear();
950
951 _objectsWithAliases.clear();
952
953 if (!collectIdsAndAliases(objectIndex: component->isInlineComponent ? componentRoots.at(i) : rootBinding->value.objectIndex))
954 return false;
955
956 component->namedObjectsInComponent.allocate(pool, container: _idToObjectIndex);
957
958 if (!resolveAliases(componentIndex: componentRoots.at(i)))
959 return false;
960 }
961
962 // Collect ids and aliases for root
963 _idToObjectIndex.clear();
964 _objectsWithAliases.clear();
965
966 collectIdsAndAliases(/*root object*/objectIndex: 0);
967
968 QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/i: 0);
969 rootComponent->namedObjectsInComponent.allocate(pool, container: _idToObjectIndex);
970
971 if (!resolveAliases(/*root object*/componentIndex: 0))
972 return false;
973
974 // Implicit component insertion may have added objects and thus we also need
975 // to extend the symmetric propertyCaches.
976 compiler->setPropertyCaches(std::move(propertyCaches));
977 compiler->setComponentRoots(componentRoots);
978
979 return true;
980}
981
982bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
983{
984 QmlIR::Object *obj = qmlObjects->at(i: objectIndex);
985
986 if (obj->idNameIndex != 0) {
987 if (_idToObjectIndex.contains(akey: obj->idNameIndex)) {
988 recordError(location: obj->locationOfIdProperty, description: tr(sourceText: "id is not unique"));
989 return false;
990 }
991 obj->id = _idToObjectIndex.count();
992 _idToObjectIndex.insert(akey: obj->idNameIndex, avalue: objectIndex);
993 }
994
995 if (obj->aliasCount() > 0)
996 _objectsWithAliases.append(t: objectIndex);
997
998 // Stop at Component boundary
999 if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
1000 return true;
1001
1002 for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
1003 if (binding->type != QV4::CompiledData::Binding::Type_Object
1004 && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
1005 && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
1006 continue;
1007
1008 if (!collectIdsAndAliases(objectIndex: binding->value.objectIndex))
1009 return false;
1010 }
1011
1012 return true;
1013}
1014
1015bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
1016{
1017 if (_objectsWithAliases.isEmpty())
1018 return true;
1019
1020 QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
1021
1022 bool atLeastOneAliasResolved;
1023 do {
1024 atLeastOneAliasResolved = false;
1025 QVector<int> pendingObjects;
1026
1027 for (int objectIndex: qAsConst(t&: _objectsWithAliases)) {
1028
1029 QQmlError error;
1030 const auto result = resolveAliasesInObject(objectIndex, error: &error);
1031
1032 if (error.isValid()) {
1033 recordError(error);
1034 return false;
1035 }
1036
1037 if (result == AllAliasesResolved) {
1038 QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(component: *qmlObjects->at(i: componentIndex), objectIndex, enginePriv: enginePrivate);
1039 if (error.isValid()) {
1040 recordError(error);
1041 return false;
1042 }
1043 atLeastOneAliasResolved = true;
1044 } else if (result == SomeAliasesResolved) {
1045 atLeastOneAliasResolved = true;
1046 pendingObjects.append(t: objectIndex);
1047 } else {
1048 pendingObjects.append(t: objectIndex);
1049 }
1050 }
1051 qSwap(value1&: _objectsWithAliases, value2&: pendingObjects);
1052 } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
1053
1054 if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
1055 const QmlIR::Object *obj = qmlObjects->at(i: _objectsWithAliases.first());
1056 for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
1057 if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) {
1058 recordError(location: alias->location, description: tr(sourceText: "Circular alias reference detected"));
1059 return false;
1060 }
1061 }
1062 }
1063
1064 return true;
1065}
1066
1067QQmlComponentAndAliasResolver::AliasResolutionResult
1068QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
1069 QQmlError *error)
1070{
1071 const QmlIR::Object * const obj = qmlObjects->at(i: objectIndex);
1072 if (!obj->aliasCount())
1073 return AllAliasesResolved;
1074
1075 int numResolvedAliases = 0;
1076 bool seenUnresolvedAlias = false;
1077
1078 for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
1079 if (alias->flags & QV4::CompiledData::Alias::Resolved)
1080 continue;
1081
1082 seenUnresolvedAlias = true;
1083
1084 const int idIndex = alias->idIndex;
1085 const int targetObjectIndex = _idToObjectIndex.value(akey: idIndex, adefaultValue: -1);
1086 if (targetObjectIndex == -1) {
1087 *error = qQmlCompileError(
1088 location: alias->referenceLocation,
1089 description: tr(sourceText: "Invalid alias reference. Unable to find id \"%1\"").arg(a: stringAt(idx: idIndex)));
1090 break;
1091 }
1092
1093 const QmlIR::Object *targetObject = qmlObjects->at(i: targetObjectIndex);
1094 Q_ASSERT(targetObject->id >= 0);
1095 alias->targetObjectId = targetObject->id;
1096 alias->aliasToLocalAlias = false;
1097
1098 const QString aliasPropertyValue = stringAt(idx: alias->propertyNameIndex);
1099
1100 QStringRef property;
1101 QStringRef subProperty;
1102
1103 const int propertySeparator = aliasPropertyValue.indexOf(c: QLatin1Char('.'));
1104 if (propertySeparator != -1) {
1105 property = aliasPropertyValue.leftRef(n: propertySeparator);
1106 subProperty = aliasPropertyValue.midRef(position: propertySeparator + 1);
1107 } else
1108 property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
1109
1110 QQmlPropertyIndex propIdx;
1111
1112 if (property.isEmpty()) {
1113 alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
1114 } else {
1115 QQmlPropertyCache *targetCache = propertyCaches.at(index: targetObjectIndex);
1116 if (!targetCache) {
1117 *error = qQmlCompileError(
1118 location: alias->referenceLocation,
1119 description: tr(sourceText: "Invalid alias target location: %1").arg(a: property.toString()));
1120 break;
1121 }
1122
1123 QQmlPropertyResolver resolver(targetCache);
1124
1125 QQmlPropertyData *targetProperty = resolver.property(name: property.toString());
1126
1127 // If it's an alias that we haven't resolved yet, try again later.
1128 if (!targetProperty) {
1129 bool aliasPointsToOtherAlias = false;
1130 int localAliasIndex = 0;
1131 for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) {
1132 if (stringAt(idx: targetAlias->nameIndex) == property) {
1133 aliasPointsToOtherAlias = true;
1134 break;
1135 }
1136 }
1137 if (aliasPointsToOtherAlias) {
1138 if (targetObjectIndex == objectIndex) {
1139 alias->localAliasIndex = localAliasIndex;
1140 alias->aliasToLocalAlias = true;
1141 alias->flags |= QV4::CompiledData::Alias::Resolved;
1142 ++numResolvedAliases;
1143 continue;
1144 }
1145
1146 // restore
1147 alias->idIndex = idIndex;
1148 // Try again later and resolve the target alias first.
1149 break;
1150 }
1151 }
1152
1153 if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
1154 *error = qQmlCompileError(
1155 location: alias->referenceLocation,
1156 description: tr(sourceText: "Invalid alias target location: %1").arg(a: property.toString()));
1157 break;
1158 }
1159
1160 propIdx = QQmlPropertyIndex(targetProperty->coreIndex());
1161
1162 if (!subProperty.isEmpty()) {
1163 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: targetProperty->propType());
1164 if (!valueTypeMetaObject) {
1165 // could be a deep alias
1166 bool isDeepAlias = subProperty.at(i: 0).isLower();
1167 if (isDeepAlias) {
1168 isDeepAlias = false;
1169 for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
1170 auto binding = *it;
1171 if (compiler->stringAt(idx: binding.propertyNameIndex) == property) {
1172 resolver = QQmlPropertyResolver(propertyCaches.at(index: binding.value.objectIndex));
1173 QQmlPropertyData *actualProperty = resolver.property(name: subProperty.toString());
1174 if (actualProperty) {
1175 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex());
1176 isDeepAlias = true;
1177 }
1178 }
1179 }
1180 }
1181 if (!isDeepAlias) {
1182 *error = qQmlCompileError(
1183 location: alias->referenceLocation,
1184 description: tr(sourceText: "Invalid alias target location: %1").arg(a: subProperty.toString()));
1185 break;
1186 }
1187 } else {
1188
1189 int valueTypeIndex =
1190 valueTypeMetaObject->indexOfProperty(name: subProperty.toString().toUtf8().constData());
1191 if (valueTypeIndex == -1) {
1192 *error = qQmlCompileError(
1193 location: alias->referenceLocation,
1194 description: tr(sourceText: "Invalid alias target location: %1").arg(a: subProperty.toString()));
1195 break;
1196 }
1197 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
1198
1199 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
1200 }
1201 } else {
1202 if (targetProperty->isQObject())
1203 alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
1204 }
1205 }
1206
1207 alias->encodedMetaPropertyIndex = propIdx.toEncoded();
1208 alias->flags |= QV4::CompiledData::Alias::Resolved;
1209 numResolvedAliases++;
1210 }
1211
1212 if (numResolvedAliases == 0)
1213 return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved;
1214
1215 return SomeAliasesResolved;
1216}
1217
1218QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
1219 : QQmlCompilePass(typeCompiler)
1220 , qmlObjects(typeCompiler->qmlObjects())
1221 , propertyCaches(typeCompiler->propertyCaches())
1222 , customParsers(typeCompiler->customParserCache())
1223 , _seenObjectWithId(false)
1224{
1225}
1226
1227bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
1228{
1229 for (int i = 0; i < qmlObjects->size(); ++i)
1230 if (qmlObjects->at(i)->isInlineComponent)
1231 scanObject(objectIndex: i);
1232 return scanObject(/*root object*/objectIndex: 0);
1233}
1234
1235bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
1236{
1237 QmlIR::Object *obj = qmlObjects->at(i: objectIndex);
1238 if (obj->idNameIndex != 0)
1239 _seenObjectWithId = true;
1240
1241 if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
1242 Q_ASSERT(obj->bindingCount() == 1);
1243 const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
1244 Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
1245 return scanObject(objectIndex: componentBinding->value.objectIndex);
1246 }
1247
1248 QQmlPropertyCache *propertyCache = propertyCaches->at(index: objectIndex);
1249 if (!propertyCache)
1250 return true;
1251
1252 QString defaultPropertyName;
1253 QQmlPropertyData *defaultProperty = nullptr;
1254 if (obj->indexOfDefaultPropertyOrAlias != -1) {
1255 QQmlPropertyCache *cache = propertyCache->parent();
1256 defaultPropertyName = cache->defaultPropertyName();
1257 defaultProperty = cache->defaultProperty();
1258 } else {
1259 defaultPropertyName = propertyCache->defaultPropertyName();
1260 defaultProperty = propertyCache->defaultProperty();
1261 }
1262
1263 QQmlCustomParser *customParser = customParsers.value(akey: obj->inheritedTypeNameIndex);
1264
1265 QQmlPropertyResolver propertyResolver(propertyCache);
1266
1267 QStringList deferredPropertyNames;
1268 {
1269 const QMetaObject *mo = propertyCache->firstCppMetaObject();
1270 const int namesIndex = mo->indexOfClassInfo(name: "DeferredPropertyNames");
1271 if (namesIndex != -1) {
1272 QMetaClassInfo classInfo = mo->classInfo(index: namesIndex);
1273 deferredPropertyNames = QString::fromUtf8(str: classInfo.value()).split(sep: QLatin1Char(','));
1274 }
1275 }
1276
1277 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
1278 QQmlPropertyData *pd = nullptr;
1279 QString name = stringAt(idx: binding->propertyNameIndex);
1280
1281 if (customParser) {
1282 if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
1283 if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
1284 binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
1285 obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
1286 continue;
1287 }
1288 } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
1289 && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
1290 obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
1291 binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
1292 continue;
1293 }
1294 }
1295
1296 if (name.isEmpty()) {
1297 pd = defaultProperty;
1298 name = defaultPropertyName;
1299 } else {
1300 if (name.constData()->isUpper())
1301 continue;
1302
1303 bool notInRevision = false;
1304 pd = propertyResolver.property(name, notInRevision: &notInRevision,
1305 check: QQmlPropertyResolver::CheckRevision);
1306 }
1307
1308 bool seenSubObjectWithId = false;
1309
1310 if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
1311 qSwap(value1&: _seenObjectWithId, value2&: seenSubObjectWithId);
1312 const bool subObjectValid = scanObject(objectIndex: binding->value.objectIndex);
1313 qSwap(value1&: _seenObjectWithId, value2&: seenSubObjectWithId);
1314 if (!subObjectValid)
1315 return false;
1316 _seenObjectWithId |= seenSubObjectWithId;
1317 }
1318
1319 if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
1320 && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(str: name)) {
1321
1322 binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
1323 obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
1324 }
1325
1326 if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
1327 || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
1328 continue;
1329
1330 if (!pd) {
1331 if (customParser) {
1332 obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
1333 binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
1334 }
1335 }
1336 }
1337
1338 return true;
1339}
1340
1341QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler)
1342 : QQmlCompilePass(typeCompiler)
1343 , qmlObjects(*typeCompiler->qmlObjects())
1344 , propertyCaches(typeCompiler->propertyCaches())
1345{
1346
1347}
1348
1349void QQmlDefaultPropertyMerger::mergeDefaultProperties()
1350{
1351 for (int i = 0; i < qmlObjects.count(); ++i)
1352 mergeDefaultProperties(objectIndex: i);
1353}
1354
1355void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
1356{
1357 QQmlPropertyCache *propertyCache = propertyCaches->at(index: objectIndex);
1358 if (!propertyCache)
1359 return;
1360
1361 QmlIR::Object *object = qmlObjects.at(i: objectIndex);
1362
1363 QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
1364 QmlIR::Binding *bindingsToReinsert = nullptr;
1365 QmlIR::Binding *tail = nullptr;
1366
1367 QmlIR::Binding *previousBinding = nullptr;
1368 QmlIR::Binding *binding = object->firstBinding();
1369 while (binding) {
1370 if (binding->propertyNameIndex == quint32(0) || stringAt(idx: binding->propertyNameIndex) != defaultProperty) {
1371 previousBinding = binding;
1372 binding = binding->next;
1373 continue;
1374 }
1375
1376 QmlIR::Binding *toReinsert = binding;
1377 binding = object->unlinkBinding(before: previousBinding, binding);
1378
1379 if (!tail) {
1380 bindingsToReinsert = toReinsert;
1381 tail = toReinsert;
1382 } else {
1383 tail->next = toReinsert;
1384 tail = tail->next;
1385 }
1386 tail->next = nullptr;
1387 }
1388
1389 binding = bindingsToReinsert;
1390 while (binding) {
1391 QmlIR::Binding *toReinsert = binding;
1392 binding = binding->next;
1393 object->insertSorted(b: toReinsert);
1394 }
1395}
1396
1397QT_END_NAMESPACE
1398

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