diff options
Diffstat (limited to 'src/qml/qml/qqmltypecompiler.cpp')
-rw-r--r-- | src/qml/qml/qqmltypecompiler.cpp | 856 |
1 files changed, 349 insertions, 507 deletions
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index 88c3a29fbf..cdabd6d5bc 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmltypecompiler_p.h" @@ -44,6 +8,8 @@ #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> #include <private/qqmlpropertyresolver_p.h> +#include <private/qqmlcomponentandaliasresolver_p.h> +#include <private/qqmlsignalnames_p.h> #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -53,19 +19,24 @@ QT_BEGIN_NAMESPACE -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, - QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) +DEFINE_BOOL_CONFIG_OPTION( + disableInternalDeferredProperties, QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES); + +Q_LOGGING_CATEGORY(lcQmlTypeCompiler, "qt.qml.typecompiler"); + +QQmlTypeCompiler::QQmlTypeCompiler( + QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML, + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher) : resolvedTypes(resolvedTypeCache) , engine(engine) , dependencyHasher(dependencyHasher) , document(parsedQML) - , typeNameCache(typeNameCache) , typeData(typeData) { } -QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() +QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() { // Build property caches and VME meta object data @@ -82,11 +53,30 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() { QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings, engine, this, imports(), typeData->typeClassName()); - QQmlError error = propertyCacheBuilder.buildMetaObjects(); - if (error.isValid()) { - recordError(error); + QQmlError cycleError = propertyCacheBuilder.verifyNoICCycle(); + if (cycleError.isValid()) { + recordError(cycleError); return nullptr; } + QQmlPropertyCacheCreatorBase::IncrementalResult result; + do { + result = propertyCacheBuilder.buildMetaObjectsIncrementally(); + const QQmlError &error = result.error; + if (error.isValid()) { + recordError(error); + return nullptr; + } else { + // Resolve component boundaries and aliases + + QQmlComponentAndAliasResolver resolver(this, enginePrivate(), &m_propertyCaches); + if (QQmlError error = resolver.resolve(result.processedRoot); error.isValid()) { + recordError(error); + return nullptr; + } + pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_propertyCaches); + pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed + } + } while (result.canResume); } { @@ -94,18 +84,6 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() merger.mergeDefaultProperties(); } - - // Resolve component boundaries and aliases - - { - // Scan for components, determine their scopes and resolve aliases within the scope. - QQmlComponentAndAliasResolver resolver(this); - if (!resolver.resolve()) - return nullptr; - - pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_propertyCaches); - } - { SignalHandlerResolver converter(this); if (!converter.resolveSignalHandlerExpressions()) @@ -134,7 +112,7 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() return nullptr; } - if (!document->javaScriptCompilationUnit.unitData()) { + if (!document->javaScriptCompilationUnit || !document->javaScriptCompilationUnit->unitData()) { // Compile JS binding expressions and signal handlers if necessary { // We can compile script strings ahead of time, but they must be compiled @@ -146,11 +124,13 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() document->jsModule.fileName = typeData->urlString(); document->jsModule.finalUrl = typeData->finalUrlString(); QmlIR::JSCodeGen v4CodeGenerator(document, engine->v4engine()->illegalNames()); - if (!v4CodeGenerator.generateCodeForComponents(componentRoots())) { - recordError(v4CodeGenerator.error()); - return nullptr; + for (QmlIR::Object *object : std::as_const(document->objects)) { + if (!v4CodeGenerator.generateRuntimeFunctions(object)) { + Q_ASSERT(v4CodeGenerator.hasError()); + recordError(v4CodeGenerator.error()); + return nullptr; + } } - document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); } @@ -162,21 +142,14 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() if (!errors.isEmpty()) return nullptr; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit - = QV4::ExecutableCompilationUnit::create(std::move( - document->javaScriptCompilationUnit)); - compilationUnit->typeNameCache = typeNameCache; - compilationUnit->resolvedTypes = *resolvedTypes; - compilationUnit->propertyCaches = std::move(m_propertyCaches); - Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount())); - return compilationUnit; + return std::move(document->javaScriptCompilationUnit); } void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) { QQmlError error; - error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line)); - error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column)); + error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line())); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column())); error.setDescription(description); error.setUrl(url()); errors << error; @@ -216,12 +189,12 @@ int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v) const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { - return document->javaScriptCompilationUnit.unitData(); + return document->javaScriptCompilationUnit->unitData(); } const QQmlImports *QQmlTypeCompiler::imports() const { - return &typeData->imports(); + return typeData->imports(); } QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const @@ -229,10 +202,9 @@ QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const return &document->objects; } -void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) +QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() { - m_propertyCaches = std::move(caches); - Q_ASSERT(m_propertyCaches.count() > 0); + return &m_propertyCaches; } const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const @@ -240,11 +212,6 @@ const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const return &m_propertyCaches; } -QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches() -{ - return std::move(m_propertyCaches); -} - QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() { return document->jsParserEngine.pool(); @@ -270,7 +237,7 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier const quint32 moduleIdx = registerString(module); const quint32 qualifierIdx = registerString(qualifier); - for (int i = 0, count = document->imports.count(); i < count; ++i) { + for (int i = 0, count = document->imports.size(); i < count; ++i) { const QV4::CompiledData::Import *existingImport = document->imports.at(i); if (existingImport->type == QV4::CompiledData::Import::ImportLibrary && existingImport->uriIndex == moduleIdx @@ -286,9 +253,9 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier document->imports.append(import); } -CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const +QQmlType QQmlTypeCompiler::qmlTypeForComponent(const QString &inlineComponentName) const { - return typeData->typeIds(objectId); + return typeData->qmlType(inlineComponentName); } QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) @@ -309,9 +276,9 @@ SignalHandlerResolver::SignalHandlerResolver(QQmlTypeCompiler *typeCompiler) bool SignalHandlerResolver::resolveSignalHandlerExpressions() { - for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) { + for (int objectIndex = 0; objectIndex < qmlObjects.size(); ++objectIndex) { const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - QQmlPropertyCache *cache = propertyCaches->at(objectIndex); + QQmlPropertyCache::ConstPtr cache = propertyCaches->at(objectIndex); if (!cache) continue; if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) { @@ -325,9 +292,9 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions() return true; } -bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object *obj, - const QString &typeName, - QQmlPropertyCache *propertyCache) +bool SignalHandlerResolver::resolveSignalHandlerExpressions( + const QmlIR::Object *obj, const QString &typeName, + const QQmlPropertyCache::ConstPtr &propertyCache) { // map from signal name defined in qml itself to list of parameters QHash<QString, QStringList> customSignals; @@ -335,40 +302,48 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { const QString bindingPropertyName = stringAt(binding->propertyNameIndex); // Attached property? - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) { const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedType(binding->propertyNameIndex); QQmlType type = typeRef ? typeRef->type() : QQmlType(); - if (!type.isValid()) - imports->resolveType(bindingPropertyName, &type, nullptr, nullptr, nullptr); + if (!type.isValid()) { + imports->resolveType( + QQmlTypeLoader::get(enginePrivate), bindingPropertyName, &type, nullptr, + nullptr); + } const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); if (!attachedType) COMPILE_EXCEPTION(binding, tr("Non-existent attached object")); - QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType); + QQmlPropertyCache::ConstPtr cache = QQmlMetaType::propertyCache(attachedType); if (!resolveSignalHandlerExpressions(attachedObj, bindingPropertyName, cache)) return false; continue; } - if (!QmlIR::IRBuilder::isSignalPropertyName(bindingPropertyName)) + QString qPropertyName; + QString signalName; + if (auto propertyName = + QQmlSignalNames::changedHandlerNameToPropertyName(bindingPropertyName)) { + qPropertyName = *propertyName; + signalName = *QQmlSignalNames::changedHandlerNameToSignalName(bindingPropertyName); + } else { + signalName = QQmlSignalNames::handlerNameToSignalName(bindingPropertyName) + .value_or(QString()); + } + if (signalName.isEmpty()) continue; QQmlPropertyResolver resolver(propertyCache); - const QString signalName = QmlIR::IRBuilder::signalNameFromSignalPropertyName( - bindingPropertyName); - - QString qPropertyName; - if (signalName.endsWith(QLatin1String("Changed"))) - qPropertyName = signalName.mid(0, signalName.length() - static_cast<int>(strlen("Changed"))); - bool notInRevision = false; - QQmlPropertyData * const signal = resolver.signal(signalName, ¬InRevision); - QQmlPropertyData * const signalPropertyData = resolver.property(signalName, /*notInRevision ptr*/nullptr); - QQmlPropertyData * const qPropertyData = !qPropertyName.isEmpty() ? resolver.property(qPropertyName) : nullptr; + const QQmlPropertyData * const signal = resolver.signal(signalName, ¬InRevision); + const QQmlPropertyData * const signalPropertyData = resolver.property(signalName, /*notInRevision ptr*/nullptr); + const QQmlPropertyData * const qPropertyData = !qPropertyName.isEmpty() ? resolver.property(qPropertyName) : nullptr; QString finalSignalHandlerPropertyName = signalName; - uint flags = QV4::CompiledData::Binding::IsSignalHandlerExpression; + QV4::CompiledData::Binding::Flag flag + = QV4::CompiledData::Binding::IsSignalHandlerExpression; const bool isPropertyObserver = !signalPropertyData && qPropertyData && qPropertyData->isBindable(); if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) { @@ -378,7 +353,7 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object bool unnamedParameter = false; QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex); - for (int i = 0; i < parameterNames.count(); ++i) { + for (int i = 0; i < parameterNames.size(); ++i) { const QString param = QString::fromUtf8(parameterNames.at(i)); if (param.isEmpty()) unnamedParameter = true; @@ -390,7 +365,7 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object } } else if (isPropertyObserver) { finalSignalHandlerPropertyName = qPropertyName; - flags = QV4::CompiledData::Binding::IsPropertyObserver; + flag = QV4::CompiledData::Binding::IsPropertyObserver; } else { if (notInRevision) { // Try assinging it as a property later @@ -438,13 +413,13 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object } // Binding object to signal means connect the signal to the object's default method. - if (binding->type == QV4::CompiledData::Binding::Type_Object) { - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; + if (bindingType == QV4::CompiledData::Binding::Type_Object) { + binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerObject); continue; } - if (binding->type != QV4::CompiledData::Binding::Type_Script) { - if (binding->type < QV4::CompiledData::Binding::Type_Script) { + if (bindingType != QV4::CompiledData::Binding::Type_Script) { + if (bindingType < QV4::CompiledData::Binding::Type_Script) { COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)")); } else { COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment")); @@ -452,7 +427,7 @@ bool SignalHandlerResolver::resolveSignalHandlerExpressions(const QmlIR::Object } binding->propertyNameIndex = compiler->registerString(finalSignalHandlerPropertyName); - binding->flags |= flags; + binding->setFlag(flag); } return true; } @@ -467,8 +442,8 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) bool QQmlEnumTypeResolver::resolveEnumBindings() { - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); + for (int i = 0; i < qmlObjects.size(); ++i) { + QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); @@ -476,18 +451,19 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() QQmlPropertyResolver resolver(propertyCache); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject - || binding->flags & QV4::CompiledData::Binding::IsPropertyObserver) + const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags(); + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject + || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver) continue; - if (binding->type != QV4::CompiledData::Binding::Type_Script) + if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; const QString propertyName = stringAt(binding->propertyNameIndex); bool notInRevision = false; - QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision); - if (!pd) + const QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision); + if (!pd || pd->isQList()) continue; if (!pd->isEnum() && pd->propType().id() != QMetaType::Int) @@ -503,37 +479,49 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, QStringView, int enumValue, bool) { - binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setType(QV4::CompiledData::Binding::Type_Number); binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue)); // binding->setNumberValueInternal((double)enumValue); - binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + binding->setFlag(QV4::CompiledData::Binding::IsResolvedEnum); return true; } -bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) +bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment( + const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache, + const QQmlPropertyData *prop, QmlIR::Binding *binding) { bool isIntProp = (prop->propType().id() == QMetaType::Int) && !prop->isEnum(); if (!prop->isEnum() && !isIntProp) return true; - if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)) - COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex))); + if (!prop->isWritable() + && !(binding->hasFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))) { + COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property") + .arg(stringAt(binding->propertyNameIndex))); + } - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script); const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); if (!string.constData()->isUpper()) return true; + // reject any "complex" expression (even simple arithmetic) + // we do this by excluding everything that is not part of a + // valid identifier or a dot + for (const QChar &c : string) + if (!(c.isLetterOrNumber() || c == u'.' || c == u'_' || c.isSpace())) + return true; + // we support one or two '.' in the enum phrase: // * <TypeName>.<EnumValue> // * <TypeName>.<ScopedEnumName>.<EnumValue> int dot = string.indexOf(QLatin1Char('.')); - if (dot == -1 || dot == string.length()-1) + if (dot == -1 || dot == string.size()-1) return true; int dot2 = string.indexOf(QLatin1Char('.'), dot+1); - if (dot2 != -1 && dot2 != string.length()-1) { + if (dot2 != -1 && dot2 != string.size()-1) { if (!string.at(dot+1).isUpper()) return true; if (string.indexOf(QLatin1Char('.'), dot2+1) != -1) @@ -557,7 +545,8 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, return true; } QQmlType type; - imports->resolveType(typeName, &type, nullptr, nullptr, nullptr); + imports->resolveType( + QQmlTypeLoader::get(compiler->enginePrivate()), typeName, &type, nullptr, nullptr); if (!type.isValid() && !isQtObject) return true; @@ -619,12 +608,13 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, QStringView enumNam if (scope != QLatin1String("Qt")) { QQmlType type; - imports->resolveType(scope, &type, nullptr, nullptr, nullptr); + imports->resolveType( + QQmlTypeLoader::get(compiler->enginePrivate()), scope, &type, nullptr, nullptr); if (!type.isValid()) return -1; if (!enumName.isEmpty()) return type.scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok); - return type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok); + return type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.size()), ok); } const QMetaObject *mo = &Qt::staticMetaObject; @@ -649,7 +639,7 @@ void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings() { scanObjectRecursively(/*root object*/0); for (int i = 0; i < qmlObjects.size(); ++i) - if (qmlObjects.at(i)->isInlineComponent) + if (qmlObjects.at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot) scanObjectRecursively(i); } @@ -659,15 +649,21 @@ void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool if (!annotateScriptBindings) annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + switch (binding->type()) { + case QV4::CompiledData::Binding::Type_Script: + if (annotateScriptBindings) { + binding->stringIndex = compiler->registerString( + compiler->bindingAsString(obj, binding->value.compiledScriptIndex)); + } + break; + case QV4::CompiledData::Binding::Type_Object: + case QV4::CompiledData::Binding::Type_AttachedProperty: + case QV4::CompiledData::Binding::Type_GroupProperty: scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings); - continue; - } else if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - if (!annotateScriptBindings) - continue; - const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - binding->stringIndex = compiler->registerString(script); + break; + default: + break; + } } } @@ -680,23 +676,23 @@ QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) void QQmlAliasAnnotator::annotateBindingsToAliases() { - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); + for (int i = 0; i < qmlObjects.size(); ++i) { + QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); QQmlPropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (!binding->isValueBinding()) continue; bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (pd && pd->isAlias()) - binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; + binding->setFlag(QV4::CompiledData::Binding::IsBindingToAlias); } } } @@ -712,21 +708,21 @@ QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler) void QQmlScriptStringScanner::scan() { const QMetaType scriptStringMetaType = QMetaType::fromType<QQmlScriptString>(); - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); + for (int i = 0; i < qmlObjects.size(); ++i) { + QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i); if (!propertyCache) continue; const QmlIR::Object *obj = qmlObjects.at(i); QQmlPropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) + if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; if (!pd || pd->propType() != scriptStringMetaType) continue; @@ -736,311 +732,99 @@ void QQmlScriptStringScanner::scan() } } -QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , pool(typeCompiler->memoryPool()) - , qmlObjects(typeCompiler->qmlObjects()) - , propertyCaches(std::move(typeCompiler->takePropertyCaches())) +template<> +void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::allocateNamedObjects( + QmlIR::Object *object) const { + object->namedObjectsInComponent.allocate(m_compiler->memoryPool(), m_idToObjectIndex); } -static bool isUsableComponent(const QMetaObject *metaObject) +template<> +bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::markAsComponent(int index) const { - // The metaObject is a component we're interested in if it either is a QQmlComponent itself - // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include - // qqmldelegatecomponent_p.h because it belongs to QtQmlModels. - - if (metaObject == &QQmlComponent::staticMetaObject) - return true; - - for (; metaObject; metaObject = metaObject->superClass()) { - if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0) - return true; - } - - return false; + m_compiler->qmlObjects()->at(index)->flags |= QV4::CompiledData::Object::IsComponent; + return true; } -void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache) +template<> +void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::setObjectId(int index) const { - QQmlPropertyResolver propertyResolver(propertyCache); - - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) - continue; - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); - auto *tr = resolvedType(targetObject->inheritedTypeNameIndex); - Q_ASSERT(tr); - - const QMetaObject *firstMetaObject = nullptr; - const auto type = tr->type(); - if (type.isValid()) - firstMetaObject = type.metaObject(); - else if (const auto compilationUnit = tr->compilationUnit()) - firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject(); - if (isUsableComponent(firstMetaObject)) - continue; - // if here, not a QQmlComponent, so needs wrapping - - QQmlPropertyData *pd = nullptr; - if (binding->propertyNameIndex != quint32(0)) { - bool notInRevision = false; - pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); - } else { - pd = defaultProperty; - } - if (!pd || !pd->isQObject()) - continue; - - // If the version is given, use it and look up by QQmlType. - // Otherwise, make sure we look up by metaobject. - // TODO: Is this correct? - QQmlPropertyCache *pc = pd->typeVersion().hasMinorVersion() - ? enginePrivate->rawPropertyCacheForType(pd->propType().id(), pd->typeVersion()) - : enginePrivate->rawPropertyCacheForType(pd->propType().id()); - const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr; - while (mo) { - if (mo == &QQmlComponent::staticMetaObject) - break; - mo = mo->superClass(); - } - - if (!mo) - continue; - - // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" - QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); - Q_ASSERT(componentType.isValid()); - const QString qualifier = QStringLiteral("QmlInternals"); - - compiler->addImport(componentType.module(), qualifier, componentType.version()); - - QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); - syntheticComponent->init( - pool, - compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), - compiler->registerString(QString()), binding->valueLocation); - syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; - - if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) { - auto typeRef = new QV4::ResolvedTypeReference; - typeRef->setType(componentType); - typeRef->setVersion(componentType.version()); - insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef); - } - - qmlObjects->append(syntheticComponent); - const int componentIndex = qmlObjects->count() - 1; - // Keep property caches symmetric - QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); - propertyCaches.append(componentCache); - - QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); - *syntheticBinding = *binding; - syntheticBinding->type = QV4::CompiledData::Binding::Type_Object; - QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false); - Q_ASSERT(error.isEmpty()); - Q_UNUSED(error); - - binding->value.objectIndex = componentIndex; - - componentRoots.append(componentIndex); - } + m_compiler->qmlObjects()->at(index)->id = m_idToObjectIndex.size(); } -bool QQmlComponentAndAliasResolver::resolve() +template<> +bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::wrapImplicitComponent(QmlIR::Binding *binding) { - // Detect real Component {} objects as well as implicitly defined components, such as - // someItemDelegate: Item {} - // In the implicit case Item is surrounded by a synthetic Component {} because the property - // on the left hand side is of QQmlComponent type. - const int objCountWithoutSynthesizedComponents = qmlObjects->count(); - for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { - QmlIR::Object *obj = qmlObjects->at(i); - if (obj->isInlineComponent) { - componentRoots.append(i); - continue; - } - QQmlPropertyCache *cache = propertyCaches.at(i); - if (obj->inheritedTypeNameIndex == 0 && !cache) - continue; - - bool isExplicitComponent = false; - - if (obj->inheritedTypeNameIndex) { - auto *tref = resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(tref); - if (tref->type().metaObject() == &QQmlComponent::staticMetaObject) - isExplicitComponent = true; - } - if (!isExplicitComponent) { - if (cache) - findAndRegisterImplicitComponents(obj, cache); - continue; - } - - obj->flags |= QV4::CompiledData::Object::IsComponent; - - if (obj->functionCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); - if (obj->signalCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); - - if (obj->bindingCount() == 0) - COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); - - const QmlIR::Binding *rootBinding = obj->firstBinding(); - - for (const QmlIR::Binding *b = rootBinding; b; b = b->next) { - if (b->propertyNameIndex != 0) - COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); - } - - if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) - COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - - // For the root object, we are going to collect ids/aliases and resolve them for as a separate - // last pass. - if (i != 0) - componentRoots.append(i); - - } - - for (int i = 0; i < componentRoots.count(); ++i) { - QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); - const QmlIR::Binding *rootBinding = component->firstBinding(); - - _idToObjectIndex.clear(); - - _objectsWithAliases.clear(); - - if (!collectIdsAndAliases(component->isInlineComponent ? componentRoots.at(i) : rootBinding->value.objectIndex)) - return false; - - component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); - - if (!resolveAliases(componentRoots.at(i))) - return false; + QQmlJS::MemoryPool *pool = m_compiler->memoryPool(); + QVector<QmlIR::Object *> *qmlObjects = m_compiler->qmlObjects(); + + // emulate "import QML 1.0" and then wrap the component in "QML.Component {}" + QQmlType componentType = QQmlMetaType::qmlType( + &QQmlComponent::staticMetaObject, QStringLiteral("QML"), + QTypeRevision::fromVersion(1, 0)); + Q_ASSERT(componentType.isValid()); + const QString qualifier = QStringLiteral("QML"); + + m_compiler->addImport(componentType.module(), qualifier, componentType.version()); + + QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); + syntheticComponent->init( + pool, + m_compiler->registerString( + qualifier + QLatin1Char('.') + componentType.elementName()), + m_compiler->registerString(QString()), binding->valueLocation); + syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; + + if (!m_compiler->resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { + auto typeRef = new QV4::ResolvedTypeReference; + typeRef->setType(componentType); + typeRef->setVersion(componentType.version()); + m_compiler->resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef); } - // Collect ids and aliases for root - _idToObjectIndex.clear(); - _objectsWithAliases.clear(); + qmlObjects->append(syntheticComponent); + const int componentIndex = qmlObjects->size() - 1; + // Keep property caches symmetric + QQmlPropertyCache::ConstPtr componentCache + = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject); + m_propertyCaches->append(componentCache); - collectIdsAndAliases(/*root object*/0); + QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); + *syntheticBinding = *binding; - QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0); - rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + // The synthetic binding inside Component has no name. It's just "Component { Foo {} }". + syntheticBinding->propertyNameIndex = 0; - if (!resolveAliases(/*root object*/0)) - return false; + syntheticBinding->setType(QV4::CompiledData::Binding::Type_Object); + QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false); + Q_ASSERT(error.isEmpty()); + Q_UNUSED(error); - // Implicit component insertion may have added objects and thus we also need - // to extend the symmetric propertyCaches. - compiler->setPropertyCaches(std::move(propertyCaches)); - compiler->setComponentRoots(componentRoots); + binding->value.objectIndex = componentIndex; + m_componentRoots.append(componentIndex); return true; } -bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) +template<> +void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveGeneralizedGroupProperty( + const CompiledObject &component, CompiledBinding *binding) { - QmlIR::Object *obj = qmlObjects->at(objectIndex); - - if (obj->idNameIndex != 0) { - if (_idToObjectIndex.contains(obj->idNameIndex)) { - recordError(obj->locationOfIdProperty, tr("id is not unique")); - return false; - } - obj->id = _idToObjectIndex.count(); - _idToObjectIndex.insert(obj->idNameIndex, objectIndex); - } - - if (obj->aliasCount() > 0) - _objectsWithAliases.append(objectIndex); - - // Stop at Component boundary - if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) - return true; - - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - if (!collectIdsAndAliases(binding->value.objectIndex)) - return false; - } - - return true; + Q_UNUSED(component); + // We cannot make it fail here. It might be a custom-parsed property + const int targetObjectIndex = m_idToObjectIndex.value(binding->propertyNameIndex, -1); + if (targetObjectIndex != -1) + m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(targetObjectIndex)); } -bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) +template<> +typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult +QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject( + const CompiledObject &component, int objectIndex, QQmlError *error) { - if (_objectsWithAliases.isEmpty()) - return true; - - QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler); + Q_UNUSED(component); - bool atLeastOneAliasResolved; - do { - atLeastOneAliasResolved = false; - QVector<int> pendingObjects; - - for (int objectIndex: qAsConst(_objectsWithAliases)) { - - QQmlError error; - const auto result = resolveAliasesInObject(objectIndex, &error); - - if (error.isValid()) { - recordError(error); - return false; - } - - if (result == AllAliasesResolved) { - QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate); - if (error.isValid()) { - recordError(error); - return false; - } - atLeastOneAliasResolved = true; - } else if (result == SomeAliasesResolved) { - atLeastOneAliasResolved = true; - pendingObjects.append(objectIndex); - } else { - pendingObjects.append(objectIndex); - } - } - qSwap(_objectsWithAliases, pendingObjects); - } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved); - - if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) { - const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first()); - for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) { - if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) { - recordError(alias->location, tr("Circular alias reference detected")); - return false; - } - } - } - - return true; -} - -QQmlComponentAndAliasResolver::AliasResolutionResult -QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, - QQmlError *error) -{ - const QmlIR::Object * const obj = qmlObjects->at(objectIndex); + const QmlIR::Object * const obj = m_compiler->objectAt(objectIndex); if (!obj->aliasCount()) return AllAliasesResolved; @@ -1048,13 +832,13 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, bool seenUnresolvedAlias = false; for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { - if (alias->flags & QV4::CompiledData::Alias::Resolved) + if (alias->hasFlag(QV4::CompiledData::Alias::Resolved)) continue; seenUnresolvedAlias = true; - const int idIndex = alias->idIndex; - const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + const int idIndex = alias->idIndex(); + const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1); if (targetObjectIndex == -1) { *error = qQmlCompileError( alias->referenceLocation, @@ -1062,10 +846,10 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, break; } - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); + const QmlIR::Object *targetObject = m_compiler->objectAt(targetObjectIndex); Q_ASSERT(targetObject->id >= 0); - alias->targetObjectId = targetObject->id; - alias->aliasToLocalAlias = false; + alias->setTargetObjectId(targetObject->id); + alias->setIsAliasToLocalAlias(false); const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); @@ -1082,9 +866,9 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlPropertyIndex propIdx; if (property.isEmpty()) { - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject); } else { - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); + QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex); if (!targetCache) { *error = qQmlCompileError( alias->referenceLocation, @@ -1094,14 +878,14 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlPropertyResolver resolver(targetCache); - QQmlPropertyData *targetProperty = resolver.property(property.toString()); + const QQmlPropertyData *targetProperty = resolver.property(property.toString()); // If it's an alias that we haven't resolved yet, try again later. if (!targetProperty) { bool aliasPointsToOtherAlias = false; int localAliasIndex = 0; for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { - if (stringAt(targetAlias->nameIndex) == property) { + if (stringAt(targetAlias->nameIndex()) == property) { aliasPointsToOtherAlias = true; break; } @@ -1109,14 +893,14 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, if (aliasPointsToOtherAlias) { if (targetObjectIndex == objectIndex) { alias->localAliasIndex = localAliasIndex; - alias->aliasToLocalAlias = true; - alias->flags |= QV4::CompiledData::Alias::Resolved; + alias->setIsAliasToLocalAlias(true); + alias->setFlag(QV4::CompiledData::Alias::Resolved); ++numResolvedAliases; continue; } // restore - alias->idIndex = idIndex; + alias->setIdIndex(idIndex); // Try again later and resolve the target alias first. break; } @@ -1132,7 +916,7 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, propIdx = QQmlPropertyIndex(targetProperty->coreIndex()); if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType(targetProperty->propType()); + const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(targetProperty->propType()); if (!valueTypeMetaObject) { // could be a deep alias bool isDeepAlias = subProperty.at(0).isLower(); @@ -1140,9 +924,9 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, isDeepAlias = false; for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) { auto binding = *it; - if (compiler->stringAt(binding.propertyNameIndex) == property) { - resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex)); - QQmlPropertyData *actualProperty = resolver.property(subProperty.toString()); + if (m_compiler->stringAt(binding.propertyNameIndex) == property) { + resolver = QQmlPropertyResolver(m_propertyCaches->at(binding.value.objectIndex)); + const QQmlPropertyData *actualProperty = resolver.property(subProperty.toString()); if (actualProperty) { propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex()); isDeepAlias = true; @@ -1172,12 +956,12 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, } } else { if (targetProperty->isQObject()) - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; + alias->setFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject); } } alias->encodedMetaPropertyIndex = propIdx.toEncoded(); - alias->flags |= QV4::CompiledData::Alias::Resolved; + alias->setFlag(QV4::CompiledData::Alias::Resolved); numResolvedAliases++; } @@ -1198,33 +982,40 @@ QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingSca bool QQmlDeferredAndCustomParserBindingScanner::scanObject() { - for (int i = 0; i < qmlObjects->size(); ++i) - if (qmlObjects->at(i)->isInlineComponent) - scanObject(i); - return scanObject(/*root object*/0); + for (int i = 0; i < qmlObjects->size(); ++i) { + if ((qmlObjects->at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot) + && !scanObject(i, ScopeDeferred::False)) { + return false; + } + } + return scanObject(/*root object*/0, ScopeDeferred::False); } -bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) +bool QQmlDeferredAndCustomParserBindingScanner::scanObject( + int objectIndex, ScopeDeferred scopeDeferred) { + using namespace QV4::CompiledData; + QmlIR::Object *obj = qmlObjects->at(objectIndex); if (obj->idNameIndex != 0) _seenObjectWithId = true; - if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) { + if (obj->flags & Object::IsComponent) { Q_ASSERT(obj->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return scanObject(componentBinding->value.objectIndex); + const Binding *componentBinding = obj->firstBinding(); + Q_ASSERT(componentBinding->type() == Binding::Type_Object); + // Components are separate from their surrounding scope. They cannot be deferred. + return scanObject(componentBinding->value.objectIndex, ScopeDeferred::False); } - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return true; QString defaultPropertyName; - QQmlPropertyData *defaultProperty = nullptr; + const QQmlPropertyData *defaultProperty = nullptr; if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); + const QQmlPropertyCache *cache = propertyCache->parent().data(); defaultPropertyName = cache->defaultPropertyName(); defaultProperty = cache->defaultProperty(); } else { @@ -1237,75 +1028,126 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) QQmlPropertyResolver propertyResolver(propertyCache); QStringList deferredPropertyNames; + QStringList immediatePropertyNames; { const QMetaObject *mo = propertyCache->firstCppMetaObject(); - const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + const int deferredNamesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + const int immediateNamesIndex = mo->indexOfClassInfo("ImmediatePropertyNames"); + if (deferredNamesIndex != -1) { + if (immediateNamesIndex != -1) { + COMPILE_EXCEPTION(obj, tr("You cannot define both DeferredPropertyNames and " + "ImmediatePropertyNames on the same type.")); + } + const QMetaClassInfo classInfo = mo->classInfo(deferredNamesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(u','); + } else if (immediateNamesIndex != -1) { + const QMetaClassInfo classInfo = mo->classInfo(immediateNamesIndex); + immediatePropertyNames = QString::fromUtf8(classInfo.value()).split(u','); + + // If the property contains an empty string, all properties shall be deferred. + if (immediatePropertyNames.isEmpty()) + immediatePropertyNames.append(QString()); } } for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - QQmlPropertyData *pd = nullptr; QString name = stringAt(binding->propertyNameIndex); if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (binding->type() == Binding::Type_AttachedProperty) { if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->setFlag(Binding::IsCustomParserBinding); + obj->flags |= Object::HasCustomParserBindings; continue; } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + } else if (QQmlSignalNames::isHandlerName(name) && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + obj->flags |= Object::HasCustomParserBindings; + binding->setFlag(Binding::IsCustomParserBinding); continue; } } - if (name.isEmpty()) { - pd = defaultProperty; - name = defaultPropertyName; - } else { - if (name.constData()->isUpper()) - continue; - - bool notInRevision = false; - pd = propertyResolver.property(name, ¬InRevision, - QQmlPropertyResolver::CheckRevision); - } + const bool hasPropertyData = [&]() { + if (name.isEmpty()) { + name = defaultPropertyName; + if (defaultProperty) + return true; + } else if (name.constData()->isUpper()) { + // Upper case names cannot be custom-parsed unless they are attached properties + // and the custom parser explicitly accepts them. See above for that case. + return false; + } else { + bool notInRevision = false; + if (propertyResolver.property( + name, ¬InRevision, QQmlPropertyResolver::CheckRevision)) { + return true; + } + } - bool seenSubObjectWithId = false; + if (!customParser) + return false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - qSwap(_seenObjectWithId, seenSubObjectWithId); - const bool subObjectValid = scanObject(binding->value.objectIndex); - qSwap(_seenObjectWithId, seenSubObjectWithId); - if (!subObjectValid) + const Binding::Flags bindingFlags = binding->flags(); + if (bindingFlags & Binding::IsSignalHandlerExpression + || bindingFlags & Binding::IsSignalHandlerObject + || bindingFlags & Binding::IsPropertyObserver) { + // These signal handlers cannot be custom-parsed. We have already established + // that the signal exists. return false; - _seenObjectWithId |= seenSubObjectWithId; - } + } - if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty - && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { + // If the property isn't found, we may want to custom-parse the binding. + obj->flags |= Object::HasCustomParserBindings; + binding->setFlag(Binding::IsCustomParserBinding); + return false; + }(); - binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; - obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; + bool seenSubObjectWithId = false; + bool isExternal = false; + if (binding->type() >= Binding::Type_Object) { + const bool isOwnProperty = hasPropertyData || binding->isAttachedProperty(); + isExternal = !isOwnProperty && binding->isGroupProperty(); + if (isOwnProperty || isExternal) { + qSwap(_seenObjectWithId, seenSubObjectWithId); + const bool subObjectValid = scanObject( + binding->value.objectIndex, + (isExternal || scopeDeferred == ScopeDeferred::True) + ? ScopeDeferred::True + : ScopeDeferred::False); + qSwap(_seenObjectWithId, seenSubObjectWithId); + if (!subObjectValid) + return false; + _seenObjectWithId |= seenSubObjectWithId; + } } - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject - || binding->flags & QV4::CompiledData::Binding::IsPropertyObserver) - continue; + bool isDeferred = false; + if (!immediatePropertyNames.isEmpty() && !immediatePropertyNames.contains(name)) { + if (seenSubObjectWithId) { + COMPILE_EXCEPTION(binding, tr("You cannot assign an id to an object assigned " + "to a deferred property.")); + } + if (isExternal || !disableInternalDeferredProperties()) + isDeferred = true; + } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { + if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty) { + if (isExternal || !disableInternalDeferredProperties()) + isDeferred = true; + } + } - if (!pd) { - if (customParser) { - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + if (binding->type() >= Binding::Type_Object) { + if (isExternal && !isDeferred && !customParser) { + COMPILE_EXCEPTION( + binding, tr("Cannot assign to non-existent property \"%1\"").arg(name)); } } + + if (isDeferred) { + binding->setFlag(Binding::IsDeferredBinding); + obj->flags |= Object::HasDeferredBindings; + } } return true; @@ -1321,13 +1163,13 @@ QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompi void QQmlDefaultPropertyMerger::mergeDefaultProperties() { - for (int i = 0; i < qmlObjects.count(); ++i) + for (int i = 0; i < qmlObjects.size(); ++i) mergeDefaultProperties(i); } void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) { - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); + QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(objectIndex); if (!propertyCache) return; |