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