aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypecompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmltypecompiler.cpp')
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp856
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, &notInRevision);
- 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, &notInRevision);
+ 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, &notInRevision);
- if (!pd)
+ const QQmlPropertyData *pd = resolver.property(propertyName, &notInRevision);
+ 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), &notInRevision) : defaultProperty;
+ const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : 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), &notInRevision) : defaultProperty;
+ const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : 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), &notInRevision);
- } 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, &notInRevision,
- 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, &notInRevision, 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;