aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/qml.pri14
-rw-r--r--src/qml/qml/qqmlirloader.cpp205
-rw-r--r--src/qml/qml/qqmlirloader_p.h81
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp101
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h843
-rw-r--r--src/qml/qml/qqmlpropertyresolver.cpp92
-rw-r--r--src/qml/qml/qqmlpropertyresolver_p.h87
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp729
-rw-r--r--src/qml/qml/qqmlpropertyvalidator_p.h91
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp1429
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h347
11 files changed, 4017 insertions, 2 deletions
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 9f79bfacdf..c625743f61 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -52,7 +52,12 @@ SOURCES += \
$$PWD/qqmlfileselector.cpp \
$$PWD/qqmlobjectcreator.cpp \
$$PWD/qqmldelayedcallqueue.cpp \
- $$PWD/qqmlloggingcategory.cpp
+ $$PWD/qqmlloggingcategory.cpp \
+ $$PWD/qqmlirloader.cpp \
+ $$PWD/qqmlpropertyresolver.cpp \
+ $$PWD/qqmltypecompiler.cpp \
+ $$PWD/qqmlpropertycachecreator.cpp \
+ $$PWD/qqmlpropertyvalidator.cpp
HEADERS += \
$$PWD/qqmlglobal_p.h \
@@ -132,7 +137,12 @@ HEADERS += \
$$PWD/qqmlfileselector.h \
$$PWD/qqmlobjectcreator_p.h \
$$PWD/qqmldelayedcallqueue_p.h \
- $$PWD/qqmlloggingcategory_p.h
+ $$PWD/qqmlloggingcategory_p.h \
+ $$PWD/qqmlirloader_p.h \
+ $$PWD/qqmlpropertyresolver_p.h \
+ $$PWD/qqmltypecompiler_p.h \
+ $$PWD/qqmlpropertycachecreator_p.h \
+ $$PWD/qqmlpropertyvalidator_p.h
qtConfig(qml-xml-http-request) {
HEADERS += \
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
new file mode 100644
index 0000000000..c2eb6da2fa
--- /dev/null
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#include "qqmlirloader_p.h"
+#include <private/qqmlirbuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlIRLoader::QQmlIRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output)
+ : unit(qmlData)
+ , output(output)
+{
+ pool = output->jsParserEngine.pool();
+}
+
+void QQmlIRLoader::load()
+{
+ output->jsGenerator.stringTable.initializeFromBackingUnit(unit);
+
+ const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit();
+
+ for (quint32 i = 0; i < qmlUnit->nImports; ++i)
+ output->imports << qmlUnit->importAt(i);
+
+ if (unit->flags & QV4::CompiledData::Unit::IsSingleton) {
+ QmlIR::Pragma *p = New<QmlIR::Pragma>();
+ p->location = QV4::CompiledData::Location();
+ p->type = QmlIR::Pragma::PragmaSingleton;
+ output->pragmas << p;
+ }
+
+ for (uint i = 0; i < qmlUnit->nObjects; ++i) {
+ const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
+ QmlIR::Object *object = loadObject(serializedObject);
+ output->objects.append(object);
+ }
+}
+
+struct FakeExpression : public QQmlJS::AST::NullExpression
+{
+ FakeExpression(int start, int length)
+ : location(start, length)
+ {}
+
+ virtual QQmlJS::AST::SourceLocation firstSourceLocation() const
+ { return location; }
+
+ virtual QQmlJS::AST::SourceLocation lastSourceLocation() const
+ { return location; }
+
+private:
+ QQmlJS::AST::SourceLocation location;
+};
+
+QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject)
+{
+ QmlIR::Object *object = pool->New<QmlIR::Object>();
+ object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex);
+
+ object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
+ object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
+ object->flags = serializedObject->flags;
+ object->id = serializedObject->id;
+ object->location = serializedObject->location;
+ object->locationOfIdProperty = serializedObject->locationOfIdProperty;
+
+ QVector<int> functionIndices;
+ functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2);
+
+ for (uint i = 0; i < serializedObject->nBindings; ++i) {
+ QmlIR::Binding *b = pool->New<QmlIR::Binding>();
+ *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i];
+ object->bindings->append(b);
+ if (b->type == QV4::CompiledData::Binding::Type_Script) {
+ functionIndices.append(b->value.compiledScriptIndex);
+ b->value.compiledScriptIndex = functionIndices.count() - 1;
+
+ QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>();
+ foe->nameIndex = 0;
+
+ QQmlJS::AST::ExpressionNode *expr;
+
+ if (b->stringIndex != quint32(0)) {
+ const int start = output->code.length();
+ const QString script = output->stringAt(b->stringIndex);
+ const int length = script.length();
+ output->code.append(script);
+ expr = new (pool) FakeExpression(start, length);
+ } else
+ expr = new (pool) QQmlJS::AST::NullExpression();
+ foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy
+ object->functionsAndExpressions->append(foe);
+ }
+ }
+
+ Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count());
+
+ for (uint i = 0; i < serializedObject->nSignals; ++i) {
+ const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i);
+ QmlIR::Signal *s = pool->New<QmlIR::Signal>();
+ s->nameIndex = serializedSignal->nameIndex;
+ s->location = serializedSignal->location;
+ s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >();
+
+ for (uint i = 0; i < serializedSignal->nParameters; ++i) {
+ QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>();
+ *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i);
+ s->parameters->append(p);
+ }
+
+ object->qmlSignals->append(s);
+ }
+
+ for (uint i = 0; i < serializedObject->nEnums; ++i) {
+ const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i);
+ QmlIR::Enum *e = pool->New<QmlIR::Enum>();
+ e->nameIndex = serializedEnum->nameIndex;
+ e->location = serializedEnum->location;
+ e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >();
+
+ for (uint i = 0; i < serializedEnum->nEnumValues; ++i) {
+ QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>();
+ *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i);
+ e->enumValues->append(v);
+ }
+
+ object->qmlEnums->append(e);
+ }
+
+ const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable();
+ for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) {
+ QmlIR::Property *p = pool->New<QmlIR::Property>();
+ *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty;
+ object->properties->append(p);
+ }
+
+ {
+ const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable();
+ for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) {
+ QmlIR::Alias *a = pool->New<QmlIR::Alias>();
+ *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias;
+ object->aliases->append(a);
+ }
+ }
+
+ const quint32_le *functionIdx = serializedObject->functionOffsetTable();
+ for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
+ QmlIR::Function *f = pool->New<QmlIR::Function>();
+ const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx);
+
+ functionIndices.append(*functionIdx);
+ f->index = functionIndices.count() - 1;
+ f->location = compiledFunction->location;
+ f->nameIndex = compiledFunction->nameIndex;
+
+ f->formals.allocate(pool, int(compiledFunction->nFormals));
+ const quint32_le *formalNameIdx = compiledFunction->formalsTable();
+ for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
+ f->formals[i] = *formalNameIdx;
+
+ object->functions->append(f);
+ }
+
+ object->runtimeFunctionIndices.allocate(pool, functionIndices);
+
+ return object;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h
new file mode 100644
index 0000000000..aa303c923f
--- /dev/null
+++ b/src/qml/qml/qqmlirloader_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#ifndef QQMLIRLOADER_P_H
+#define QQMLIRLOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4compileddata_p.h>
+#include <private/qqmljsmemorypool_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QmlIR {
+struct Document;
+struct Object;
+}
+
+struct Q_QML_PRIVATE_EXPORT QQmlIRLoader {
+ QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output);
+
+ void load();
+
+private:
+ QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject);
+
+ template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); }
+
+ const QV4::CompiledData::Unit *unit;
+ QmlIR::Document *output;
+ QQmlJS::MemoryPool *pool;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLIRLOADER_P_H
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
new file mode 100644
index 0000000000..bd4f2a0612
--- /dev/null
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qqmlpropertycachecreator_p.h"
+
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
+
+QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
+ const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache)
+ : referencingObjectIndex(referencingObjectIndex)
+ , instantiatingBinding(instantiatingBinding)
+ , instantiatingPropertyName(instantiatingPropertyName)
+ , referencingObjectPropertyCache(referencingObjectPropertyCache)
+{
+}
+
+bool QQmlBindingInstantiationContext::resolveInstantiatingProperty()
+{
+ if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ return true;
+
+ Q_ASSERT(referencingObjectIndex >= 0);
+ Q_ASSERT(referencingObjectPropertyCache);
+ Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
+
+ bool notInRevision = false;
+ instantiatingProperty = QQmlPropertyResolver(referencingObjectPropertyCache)
+ .property(instantiatingPropertyName, &notInRevision,
+ QQmlPropertyResolver::IgnoreRevision);
+ return instantiatingProperty != nullptr;
+}
+
+QQmlRefPointer<QQmlPropertyCache> QQmlBindingInstantiationContext::instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const
+{
+ if (instantiatingProperty) {
+ if (instantiatingProperty->isQObject()) {
+ return enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType(), instantiatingProperty->typeMinorVersion());
+ } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType())) {
+ return enginePrivate->cache(vtmo, instantiatingProperty->typeMinorVersion());
+ }
+ }
+ return QQmlRefPointer<QQmlPropertyCache>();
+}
+
+void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const
+{
+ for (QQmlBindingInstantiationContext pendingBinding: *this) {
+ const int groupPropertyObjectIndex = pendingBinding.instantiatingBinding->value.objectIndex;
+
+ if (propertyCaches->at(groupPropertyObjectIndex))
+ continue;
+
+ if (!pendingBinding.resolveInstantiatingProperty())
+ continue;
+
+ auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate);
+ propertyCaches->set(groupPropertyObjectIndex, cache);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
new file mode 100644
index 0000000000..28eea27675
--- /dev/null
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -0,0 +1,843 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QQMLPROPERTYCACHECREATOR_P_H
+#define QQMLPROPERTYCACHECREATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlmetaobject_p.h>
+#include <private/qqmlpropertyresolver_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlBindingInstantiationContext {
+ QQmlBindingInstantiationContext() {}
+ QQmlBindingInstantiationContext(int referencingObjectIndex,
+ const QV4::CompiledData::Binding *instantiatingBinding,
+ const QString &instantiatingPropertyName,
+ QQmlPropertyCache *referencingObjectPropertyCache);
+
+ bool resolveInstantiatingProperty();
+ QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const;
+
+ int referencingObjectIndex = -1;
+ const QV4::CompiledData::Binding *instantiatingBinding = nullptr;
+ QString instantiatingPropertyName;
+ QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache;
+ QQmlPropertyData *instantiatingProperty = nullptr;
+};
+
+struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext>
+{
+ void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const;
+};
+
+struct QQmlPropertyCacheCreatorBase
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
+public:
+ static QAtomicInt classIndexCounter;
+};
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
+ QQmlEnginePrivate *enginePrivate,
+ const ObjectContainer *objectContainer, const QQmlImports *imports);
+
+ QQmlCompileError buildMetaObjects();
+
+protected:
+ QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
+ QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
+
+ QString stringAt(int index) const { return objectContainer->stringAt(index); }
+
+ QQmlEnginePrivate * const enginePrivate;
+ const ObjectContainer * const objectContainer;
+ const QQmlImports * const imports;
+ QQmlPropertyCacheVector *propertyCaches;
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
+ QQmlEnginePrivate *enginePrivate,
+ const ObjectContainer *objectContainer, const QQmlImports *imports)
+ : enginePrivate(enginePrivate)
+ , objectContainer(objectContainer)
+ , imports(imports)
+ , propertyCaches(propertyCaches)
+ , pendingGroupPropertyBindings(pendingGroupPropertyBindings)
+{
+ propertyCaches->resize(objectContainer->objectCount());
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
+{
+ QQmlBindingInstantiationContext context;
+ return buildMetaObjectRecursively(/*root object*/0, context);
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
+{
+ const CompiledObject *obj = objectContainer->objectAt(objectIndex);
+
+ bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0;
+ if (!needVMEMetaObject) {
+ auto binding = obj->bindingsBegin();
+ auto end = obj->bindingsEnd();
+ for ( ; binding != end; ++binding) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ // If the on assignment is inside a group property, we need to distinguish between QObject based
+ // group properties and value type group properties. For the former the base type is derived from
+ // the property that references us, for the latter we only need a meta-object on the referencing object
+ // because interceptors can't go to the shared value type instances.
+ if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) {
+ if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) {
+ const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
+ auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ if (error.isSet())
+ return error;
+ }
+ } else {
+ // On assignments are implemented using value interceptors, which require a VME meta object.
+ needVMEMetaObject = true;
+ }
+ break;
+ }
+ }
+ }
+
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
+ {
+ QQmlCompileError error;
+ baseTypeCache = propertyCacheForObject(obj, context, &error);
+ if (error.isSet())
+ return error;
+ }
+
+ if (baseTypeCache) {
+ if (needVMEMetaObject) {
+ QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache);
+ if (error.isSet())
+ return error;
+ } else {
+ propertyCaches->set(objectIndex, baseTypeCache);
+ }
+ }
+
+ if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) {
+ auto binding = obj->bindingsBegin();
+ auto end = obj->bindingsEnd();
+ for ( ; binding != end; ++binding)
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+
+ // Binding to group property where we failed to look up the type of the
+ // property? Possibly a group property that is an alias that's not resolved yet.
+ // Let's attempt to resolve it after we're done with the aliases and fill in the
+ // propertyCaches entry then.
+ if (!context.resolveInstantiatingProperty())
+ pendingGroupPropertyBindings->append(context);
+
+ QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context);
+ if (error.isSet())
+ return error;
+ }
+ }
+
+ QQmlCompileError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+{
+ if (context.instantiatingProperty) {
+ return context.instantiatingPropertyCache(enginePrivate);
+ } else if (obj->inheritedTypeNameIndex != 0) {
+ auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->isFullyDynamicType) {
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
+ return nullptr;
+ }
+ if (obj->signalCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
+ return nullptr;
+ }
+ if (obj->functionCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
+ return nullptr;
+ }
+ }
+
+ return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) {
+ auto *typeRef = objectContainer->resolvedType(
+ context.instantiatingBinding->propertyNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlType qmltype = typeRef->type;
+ if (!qmltype.isValid()) {
+ QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
+ if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
+ if (qmltype.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+ qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
+ }
+ }
+ }
+
+ const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
+ if (!attachedMo) {
+ *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
+ return nullptr;
+ }
+ return enginePrivate->cache(attachedMo);
+ }
+ return nullptr;
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
+{
+ QQmlRefPointer<QQmlPropertyCache> cache;
+ cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
+ obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
+ obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount()));
+
+ propertyCaches->set(objectIndex, cache);
+ propertyCaches->setNeedsVMEMetaObject(objectIndex);
+
+ struct TypeData {
+ QV4::CompiledData::Property::Type dtype;
+ int metaType;
+ } builtinTypes[] = {
+ { QV4::CompiledData::Property::Var, QMetaType::QVariant },
+ { QV4::CompiledData::Property::Variant, QMetaType::QVariant },
+ { QV4::CompiledData::Property::Int, QMetaType::Int },
+ { QV4::CompiledData::Property::Bool, QMetaType::Bool },
+ { QV4::CompiledData::Property::Real, QMetaType::Double },
+ { QV4::CompiledData::Property::String, QMetaType::QString },
+ { QV4::CompiledData::Property::Url, QMetaType::QUrl },
+ { QV4::CompiledData::Property::Color, QMetaType::QColor },
+ { QV4::CompiledData::Property::Font, QMetaType::QFont },
+ { QV4::CompiledData::Property::Time, QMetaType::QTime },
+ { QV4::CompiledData::Property::Date, QMetaType::QDate },
+ { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
+ { QV4::CompiledData::Property::Rect, QMetaType::QRectF },
+ { QV4::CompiledData::Property::Point, QMetaType::QPointF },
+ { QV4::CompiledData::Property::Size, QMetaType::QSizeF },
+ { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
+ { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
+ { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
+ { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
+ { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
+};
+ static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
+
+ QByteArray newClassName;
+
+ if (objectIndex == /*root object*/0) {
+ const QString path = objectContainer->url().path();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ if (lastSlash > -1) {
+ const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
+ if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
+ newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
+ QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+ }
+ }
+ if (newClassName.isEmpty()) {
+ newClassName = QQmlMetaObject(baseTypeCache.data()).className();
+ newClassName.append("_QML_");
+ newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
+ }
+
+ cache->_dynamicClassName = newClassName;
+
+ int varPropCount = 0;
+
+ QQmlPropertyResolver resolver(baseTypeCache);
+
+ auto p = obj->propertiesBegin();
+ auto pend = obj->propertiesEnd();
+ for ( ; p != pend; ++p) {
+ if (p->type == QV4::CompiledData::Property::Var)
+ varPropCount++;
+
+ bool notInRevision = false;
+ QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ auto a = obj->aliasesBegin();
+ auto aend = obj->aliasesEnd();
+ for ( ; a != aend; ++a) {
+ bool notInRevision = false;
+ QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ int effectivePropertyIndex = cache->propertyIndexCacheStart;
+ int effectiveMethodIndex = cache->methodIndexCacheStart;
+
+ // For property change signal override detection.
+ // We prepopulate a set of signal names which already exist in the object,
+ // and throw an error if there is a signal/method defined as an override.
+ QSet<QString> seenSignals;
+ seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
+ QQmlPropertyCache *parentCache = cache.data();
+ while ((parentCache = parentCache->parent())) {
+ if (int pSigCount = parentCache->signalCount()) {
+ int pSigOffset = parentCache->signalOffset();
+ for (int i = pSigOffset; i < pSigCount; ++i) {
+ QQmlPropertyData *currPSig = parentCache->signal(i);
+ // XXX TODO: find a better way to get signal name from the property data :-/
+ for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
+ iter != parentCache->stringCache.end(); ++iter) {
+ if (currPSig == (*iter).second) {
+ seenSignals.insert(iter.key());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Set up notify signals for properties - first normal, then alias
+ p = obj->propertiesBegin();
+ pend = obj->propertiesEnd();
+ for ( ; p != pend; ++p) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
+ seenSignals.insert(changedSigName);
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ a = obj->aliasesBegin();
+ aend = obj->aliasesEnd();
+ for ( ; a != aend; ++a) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed");
+ seenSignals.insert(changedSigName);
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ auto e = obj->enumsBegin();
+ auto eend = obj->enumsEnd();
+ for ( ; e != eend; ++e) {
+ const int enumValueCount = e->enumValueCount();
+ QVector<QQmlEnumValue> values;
+ values.reserve(enumValueCount);
+
+ auto enumValue = e->enumValuesBegin();
+ auto end = e->enumValuesEnd();
+ for ( ; enumValue != end; ++enumValue)
+ values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value));
+
+ cache->appendEnum(stringAt(e->nameIndex), values);
+ }
+
+ // Dynamic signals
+ auto s = obj->signalsBegin();
+ auto send = obj->signalsEnd();
+ for ( ; s != send; ++s) {
+ const int paramCount = s->parameterCount();
+
+ QList<QByteArray> names;
+ names.reserve(paramCount);
+ QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
+
+ if (paramCount) {
+ paramTypes[0] = paramCount;
+
+ int i = 0;
+ auto param = s->parametersBegin();
+ auto end = s->parametersEnd();
+ for ( ; param != end; ++param, ++i) {
+ names.append(stringAt(param->nameIndex).toUtf8());
+ if (param->type < builtinTypeCount) {
+ // built-in type
+ paramTypes[i + 1] = builtinTypes[param->type].metaType;
+ } else {
+ // lazily resolved type
+ Q_ASSERT(param->type == QV4::CompiledData::Property::Custom);
+ const QString customTypeName = stringAt(param->customTypeNameIndex);
+ QQmlType qmltype;
+ if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr))
+ return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
+
+ if (qmltype.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+
+ paramTypes[i + 1] = compilationUnit->metaTypeId;
+ } else {
+ paramTypes[i + 1] = qmltype.typeId();
+ }
+ }
+ }
+ }
+
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+ if (paramCount)
+ flags.hasArguments = true;
+
+ QString signalName = stringAt(s->nameIndex);
+ if (seenSignals.contains(signalName))
+ return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
+ seenSignals.insert(signalName);
+
+ cache->appendSignal(signalName, flags, effectiveMethodIndex++,
+ paramCount?paramTypes.constData():nullptr, names);
+ }
+
+
+ // Dynamic slots
+ auto function = objectContainer->objectFunctionsBegin(obj);
+ auto fend = objectContainer->objectFunctionsEnd(obj);
+ for ( ; function != fend; ++function) {
+ auto flags = QQmlPropertyData::defaultSlotFlags();
+
+ const QString slotName = stringAt(function->nameIndex);
+ if (seenSignals.contains(slotName))
+ return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
+ // Note: we don't append slotName to the seenSignals list, since we don't
+ // protect against overriding change signals or methods with properties.
+
+ QList<QByteArray> parameterNames;
+ auto formal = function->formalsBegin();
+ auto end = function->formalsEnd();
+ for ( ; formal != end; ++formal) {
+ flags.hasArguments = true;
+ parameterNames << stringAt(*formal).toUtf8();
+ }
+
+ cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
+ }
+
+
+ // Dynamic properties
+ int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
+ int propertyIdx = 0;
+ p = obj->propertiesBegin();
+ pend = obj->propertiesEnd();
+ for ( ; p != pend; ++p, ++propertyIdx) {
+ int propertyType = 0;
+ int propertTypeMinorVersion = 0;
+ QQmlPropertyData::Flags propertyFlags;
+
+ if (p->type == QV4::CompiledData::Property::Var) {
+ propertyType = QMetaType::QVariant;
+ propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
+ } else if (p->type < builtinTypeCount) {
+ propertyType = builtinTypes[p->type].metaType;
+
+ if (p->type == QV4::CompiledData::Property::Variant)
+ propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
+ } else {
+ Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList ||
+ p->type == QV4::CompiledData::Property::Custom);
+
+ QQmlType qmltype;
+ if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) {
+ return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
+ }
+
+ Q_ASSERT(qmltype.isValid());
+ if (qmltype.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+
+ if (p->type == QV4::CompiledData::Property::Custom) {
+ propertyType = compilationUnit->metaTypeId;
+ } else {
+ propertyType = compilationUnit->listMetaTypeId;
+ }
+ } else {
+ if (p->type == QV4::CompiledData::Property::Custom) {
+ propertyType = qmltype.typeId();
+ propertTypeMinorVersion = qmltype.minorVersion();
+ } else {
+ propertyType = qmltype.qListTypeId();
+ }
+ }
+
+ if (p->type == QV4::CompiledData::Property::Custom)
+ propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
+ else
+ propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ }
+
+ if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
+ propertyFlags.isWritable = true;
+
+
+ QString propertyName = stringAt(p->nameIndex);
+ if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
+ cache->_defaultPropertyName = propertyName;
+ cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ propertyType, propertTypeMinorVersion, effectiveSignalIndex);
+
+ effectiveSignalIndex++;
+ }
+
+ QQmlCompileError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheAliasCreator
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
+
+ void appendAliasPropertiesToMetaObjects();
+
+ QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
+
+private:
+ void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
+ QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags);
+
+ void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
+
+ int objectForId(const CompiledObject &component, int id) const;
+
+ QQmlPropertyCacheVector *propertyCaches;
+ const ObjectContainer *objectContainer;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
+ : propertyCaches(propertyCaches)
+ , objectContainer(objectContainer)
+{
+
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
+{
+ // skip the root object (index 0) as that one does not have a first object index originating
+ // from a binding.
+ for (int i = 1; i < objectContainer->objectCount(); ++i) {
+ const CompiledObject &component = *objectContainer->objectAt(i);
+ if (!(component.flags & QV4::CompiledData::Object::IsComponent))
+ continue;
+
+ const auto rootBinding = component.bindingsBegin();
+ appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
+ }
+
+ const int rootObjectIndex = 0;
+ appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex)
+{
+ QVector<int> objectsWithAliases;
+ collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
+ if (objectsWithAliases.isEmpty())
+ return;
+
+ const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
+ auto alias = object.aliasesBegin();
+ auto end = object.aliasesEnd();
+ for ( ; alias != end; ++alias) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+
+ const int targetObjectIndex = objectForId(component, alias->targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+
+ if (alias->aliasToLocalAlias)
+ continue;
+
+ if (alias->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ if (!targetProperty)
+ return false;
+ }
+ return true;
+ };
+
+ do {
+ QVector<int> pendingObjects;
+
+ for (int objectIndex: qAsConst(objectsWithAliases)) {
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+
+ if (allAliasTargetsExist(object)) {
+ appendAliasesToPropertyCache(component, objectIndex);
+ } else {
+ pendingObjects.append(objectIndex);
+ }
+
+ }
+ qSwap(objectsWithAliases, pendingObjects);
+ } while (!objectsWithAliases.isEmpty());
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (object.aliasCount() > 0)
+ objectsWithAliases->append(objectIndex);
+
+ // Stop at Component boundary
+ if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
+ return;
+
+ auto binding = object.bindingsBegin();
+ auto end = object.bindingsEnd();
+ for (; binding != end; ++binding) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Object
+ && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
+ && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ continue;
+
+ collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ }
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
+ QQmlPropertyData::Flags *propertyFlags)
+{
+ *type = 0;
+ bool writable = false;
+ bool resettable = false;
+
+ propertyFlags->isAlias = true;
+
+ if (alias.aliasToLocalAlias) {
+ const QV4::CompiledData::Alias *lastAlias = &alias;
+ QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
+
+ do {
+ const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+ const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
+ Q_ASSERT(targetObject);
+
+ auto nextAlias = targetObject->aliasesBegin();
+ for (uint i = 0; i < lastAlias->localAliasIndex; ++i)
+ ++nextAlias;
+
+ const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
+ if (seenAliases.contains(targetAlias)) {
+ return QQmlCompileError(targetAlias->location,
+ QQmlPropertyCacheCreatorBase::tr("Cyclic alias"));
+ }
+
+ seenAliases.append(targetAlias);
+ lastAlias = targetAlias;
+ } while (lastAlias->aliasToLocalAlias);
+
+ return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags);
+ }
+
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+ const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
+
+ if (alias.encodedMetaPropertyIndex == -1) {
+ Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
+ auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
+ if (!typeRef) {
+ // Can be caused by the alias target not being a valid id or property. E.g.:
+ // property alias dataValue: dataVal
+ // invalidAliasComponent { id: dataVal }
+ return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
+
+ if (typeRef->type.isValid())
+ *type = typeRef->type.typeId();
+ else
+ *type = typeRef->compilationUnit->metaTypeId;
+
+ *minorVersion = typeRef->minorVersion;
+
+ propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
+ } else {
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
+ int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
+
+ QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ Q_ASSERT(targetProperty);
+
+ *type = targetProperty->propType();
+
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
+ if (valueTypeIndex != -1) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
+ if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
+ *type = QVariant::Int;
+ else
+ *type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ } else {
+ if (targetProperty->isEnum()) {
+ *type = QVariant::Int;
+ } else {
+ // Copy type flags
+ propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
+
+ if (targetProperty->isVarProperty())
+ propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ }
+ }
+ }
+
+ propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable;
+ propertyFlags->isResettable = resettable;
+ return QQmlCompileError();
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex)
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (!object.aliasCount())
+ return QQmlCompileError();
+
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
+ Q_ASSERT(propertyCache);
+
+ int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
+ int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
+
+ int aliasIndex = 0;
+ auto alias = object.aliasesBegin();
+ auto end = object.aliasesEnd();
+ for ( ; alias != end; ++alias, ++aliasIndex) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+
+ int type = 0;
+ int minorVersion = 0;
+ QQmlPropertyData::Flags propertyFlags;
+ QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags);
+ if (error.isSet())
+ return error;
+
+ const QString propertyName = objectContainer->stringAt(alias->nameIndex);
+
+ if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ propertyCache->_defaultPropertyName = propertyName;
+
+ propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ type, minorVersion, effectiveSignalIndex++);
+ }
+
+ return QQmlCompileError();
+}
+
+template <typename ObjectContainer>
+inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
+{
+ for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
+ if (candidate.id == id)
+ return candidateIndex;
+ }
+ return -1;
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYCACHECREATOR_P_H
diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp
new file mode 100644
index 0000000000..90eaca0b90
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyresolver.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#include "qqmlpropertyresolver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlPropertyData *QQmlPropertyResolver::property(const QString &name, bool *notInRevision,
+ RevisionCheck check) const
+{
+ if (notInRevision) *notInRevision = false;
+
+ QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
+
+ // Find the first property
+ while (d && d->isFunction())
+ d = cache->overrideData(d);
+
+ if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) {
+ if (notInRevision) *notInRevision = true;
+ return nullptr;
+ } else {
+ return d;
+ }
+}
+
+
+QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *notInRevision) const
+{
+ if (notInRevision) *notInRevision = false;
+
+ QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
+ if (notInRevision) *notInRevision = false;
+
+ while (d && !(d->isFunction()))
+ d = cache->overrideData(d);
+
+ if (d && !cache->isAllowedInRevision(d)) {
+ if (notInRevision) *notInRevision = true;
+ return nullptr;
+ } else if (d && d->isSignal()) {
+ return d;
+ }
+
+ if (name.endsWith(QLatin1String("Changed"))) {
+ QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed")));
+
+ d = property(propName, notInRevision);
+ if (d)
+ return cache->signal(d->notifyIndex());
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertyresolver_p.h b/src/qml/qml/qqmlpropertyresolver_p.h
new file mode 100644
index 0000000000..df857f242e
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyresolver_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#ifndef QQMLPROPERTYRESOLVER_P_H
+#define QQMLPROPERTYRESOLVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlrefcount_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_QML_EXPORT QQmlPropertyResolver
+{
+ QQmlPropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
+ : cache(cache)
+ {}
+
+ QQmlPropertyData *property(int index) const
+ {
+ return cache->property(index);
+ }
+
+ enum RevisionCheck {
+ CheckRevision,
+ IgnoreRevision
+ };
+
+ QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr,
+ RevisionCheck check = CheckRevision) const;
+
+ // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
+ QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
+
+ QQmlRefPointer<QQmlPropertyCache> cache;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYRESOLVER_P_H
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
new file mode 100644
index 0000000000..71d5318652
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -0,0 +1,729 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qqmlpropertyvalidator_p.h"
+
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlstringconverters_p.h>
+#include <private/qqmlpropertyresolver_p.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
+ : enginePrivate(enginePrivate)
+ , compilationUnit(compilationUnit)
+ , imports(imports)
+ , qmlUnit(compilationUnit->unitData())
+ , propertyCaches(compilationUnit->propertyCaches)
+ , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject)
+{
+ bindingPropertyDataPerObject->resize(compilationUnit->objectCount());
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::validate()
+{
+ return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr);
+}
+
+typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
+
+struct BindingFinder
+{
+ bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const
+ {
+ return name < binding->propertyNameIndex;
+ }
+ bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const
+ {
+ return binding->propertyNameIndex < name;
+ }
+ bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const
+ {
+ return lhs->propertyNameIndex < rhs->propertyNameIndex;
+ }
+};
+
+QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
+{
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
+
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->nBindings == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
+ Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ return validateObject(componentBinding->value.objectIndex, componentBinding);
+ }
+
+ QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+ if (!propertyCache)
+ return QVector<QQmlCompileError>();
+
+ QQmlCustomParser *customParser = nullptr;
+ if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
+ if (typeRef->type.isValid())
+ customParser = typeRef->type.customParser();
+ }
+
+ QList<const QV4::CompiledData::Binding*> customBindings;
+
+ // Collect group properties first for sanity checking
+ // vector values are sorted by property name string index.
+ GroupPropertyVector groupProperties;
+ const QV4::CompiledData::Binding *binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ if (!binding->isGroupProperty())
+ continue;
+
+ if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ continue;
+
+ if (populatingValueTypeGroupProperty) {
+ return recordError(binding->location, tr("Property assignment expected"));
+ }
+
+ GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
+ groupProperties.insert(pos, binding);
+ }
+
+ QQmlPropertyResolver propertyResolver(propertyCache);
+
+ QString defaultPropertyName;
+ QQmlPropertyData *defaultProperty = nullptr;
+ if (obj->indexOfDefaultPropertyOrAlias != -1) {
+ QQmlPropertyCache *cache = propertyCache->parent();
+ defaultPropertyName = cache->defaultPropertyName();
+ defaultProperty = cache->defaultProperty();
+ } else {
+ defaultPropertyName = propertyCache->defaultPropertyName();
+ defaultProperty = propertyCache->defaultProperty();
+ }
+
+ QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+
+ binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ QString name = stringAt(binding->propertyNameIndex);
+
+ if (customParser) {
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
+ customBindings << binding;
+ continue;
+ }
+ } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
+ customBindings << binding;
+ continue;
+ }
+ }
+
+ bool bindingToDefaultProperty = false;
+ bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
+
+ bool notInRevision = false;
+ QQmlPropertyData *pd = nullptr;
+ if (!name.isEmpty()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
+ pd = propertyResolver.signal(name, &notInRevision);
+ } else {
+ pd = propertyResolver.property(name, &notInRevision,
+ QQmlPropertyResolver::CheckRevision);
+ }
+
+ if (notInRevision) {
+ QString typeName = stringAt(obj->inheritedTypeNameIndex);
+ auto *objectType = resolvedType(obj->inheritedTypeNameIndex);
+ if (objectType && objectType->type.isValid()) {
+ return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion));
+ } else {
+ return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
+ }
+ }
+ } else {
+ if (isGroupProperty)
+ return recordError(binding->location, tr("Cannot assign a value directly to a grouped property"));
+
+ pd = defaultProperty;
+ name = defaultPropertyName;
+ bindingToDefaultProperty = true;
+ }
+
+ if (pd)
+ collectedBindingPropertyData[i] = pd;
+
+ if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
+ QQmlType type;
+ QQmlImportNamespace *typeNamespace = nullptr;
+ imports.resolveType(stringAt(binding->propertyNameIndex), &type, nullptr, nullptr, &typeNamespace);
+ if (typeNamespace)
+ return recordError(binding->location, tr("Invalid use of namespace"));
+ return recordError(binding->location, tr("Invalid attached object assignment"));
+ }
+
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
+ const bool populatingValueTypeGroupProperty
+ = pd
+ && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())
+ && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment);
+ const QVector<QQmlCompileError> subObjectValidatorErrors
+ = validateObject(binding->value.objectIndex, binding,
+ populatingValueTypeGroupProperty);
+ if (!subObjectValidatorErrors.isEmpty())
+ return subObjectValidatorErrors;
+ }
+
+ // Signal handlers were resolved and checked earlier in the signal handler conversion pass.
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ continue;
+
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
+ return recordError(binding->location, tr("Attached properties cannot be used here"));
+ }
+ continue;
+ }
+
+ if (pd) {
+ GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
+ const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex);
+
+ if (!pd->isWritable()
+ && !pd->isQList()
+ && !binding->isGroupProperty()
+ && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
+ ) {
+
+ if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
+ return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
+ return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
+ }
+
+ if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
+ QString error;
+ if (pd->propType() == qMetaTypeId<QQmlScriptString>())
+ error = tr( "Cannot assign multiple values to a script property");
+ else
+ error = tr( "Cannot assign multiple values to a singular property");
+ return recordError(binding->valueLocation, error);
+ }
+
+ if (!bindingToDefaultProperty
+ && !binding->isGroupProperty()
+ && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ && assigningToGroupProperty) {
+ QV4::CompiledData::Location loc = binding->valueLocation;
+ if (loc < (*assignedGroupProperty)->valueLocation)
+ loc = (*assignedGroupProperty)->valueLocation;
+
+ if (pd && QQmlValueTypeFactory::isValueType(pd->propType()))
+ return recordError(loc, tr("Property has already been assigned a value"));
+ return recordError(loc, tr("Cannot assign a value directly to a grouped property"));
+ }
+
+ if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding);
+ if (bindingError.isSet())
+ return recordError(bindingError);
+ } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ QQmlCompileError bindingError = validateObjectBinding(pd, name, binding);
+ if (bindingError.isSet())
+ return recordError(bindingError);
+ } else if (binding->isGroupProperty()) {
+ if (QQmlValueTypeFactory::isValueType(pd->propType())) {
+ if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) {
+ if (!pd->isWritable()) {
+ return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
+ }
+ } else {
+ return recordError(binding->location, tr("Invalid grouped property access"));
+ }
+ } else {
+ if (!enginePrivate->propertyCacheForType(pd->propType())) {
+ return recordError(binding->location,
+ tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type")
+ .arg(name)
+ .arg(QString::fromLatin1(QMetaType::typeName(pd->propType())))
+ );
+ }
+ }
+ }
+ } else {
+ if (customParser) {
+ customBindings << binding;
+ continue;
+ }
+ if (bindingToDefaultProperty) {
+ return recordError(binding->location, tr("Cannot assign to non-existent default property"));
+ } else {
+ return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name));
+ }
+ }
+ }
+
+ if (obj->idNameIndex) {
+ if (populatingValueTypeGroupProperty)
+ return recordError(obj->locationOfIdProperty, tr("Invalid use of id property with a value type"));
+
+ bool notInRevision = false;
+ collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
+ }
+
+ if (customParser && !customBindings.isEmpty()) {
+ customParser->clearErrors();
+ customParser->validator = this;
+ customParser->engine = enginePrivate;
+ customParser->imports = &imports;
+ customParser->verifyBindings(compilationUnit, customBindings);
+ customParser->validator = nullptr;
+ customParser->engine = nullptr;
+ customParser->imports = (QQmlImports*)nullptr;
+ QVector<QQmlCompileError> parserErrors = customParser->errors();
+ if (!parserErrors.isEmpty())
+ return parserErrors;
+ }
+
+ (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData;
+
+ QVector<QQmlCompileError> noError;
+ return noError;
+}
+
+QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
+{
+ if (property->isQList()) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists"));
+ }
+
+ QQmlCompileError noError;
+
+ if (property->isEnum()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
+ return noError;
+
+ QString value = compilationUnit->bindingValueAsString(binding);
+ QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
+ bool ok;
+ if (p.isFlagType()) {
+ p.enumerator().keysToValue(value.toUtf8().constData(), &ok);
+ } else
+ p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
+
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
+ }
+ return noError;
+ }
+
+ auto warnOrError = [&](const QString &error) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ QQmlError warning;
+ warning.setUrl(compilationUnit->url());
+ warning.setLine(binding->valueLocation.line);
+ warning.setColumn(binding->valueLocation.column);
+ warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML "
+ "is deprecated. This will become a compile error in "
+ "future versions of Qt."));
+ enginePrivate->warning(warning);
+ return noError;
+ }
+ return QQmlCompileError(binding->valueLocation, error);
+ };
+
+ switch (property->propType()) {
+ case QMetaType::QVariant:
+ break;
+ case QVariant::String: {
+ if (!binding->evaluatesToString()) {
+ return warnOrError(tr("Invalid property assignment: string expected"));
+ }
+ }
+ break;
+ case QVariant::StringList: {
+ if (!binding->evaluatesToString()) {
+ return warnOrError(tr("Invalid property assignment: string or string list expected"));
+ }
+ }
+ break;
+ case QVariant::ByteArray: {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return warnOrError(tr("Invalid property assignment: byte array expected"));
+ }
+ }
+ break;
+ case QVariant::Url: {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return warnOrError(tr("Invalid property assignment: url expected"));
+ }
+ }
+ break;
+ case QVariant::UInt: {
+ if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ if (double(uint(d)) == d)
+ return noError;
+ }
+ return warnOrError(tr("Invalid property assignment: unsigned int expected"));
+ }
+ break;
+ case QVariant::Int: {
+ if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ double d = compilationUnit->bindingValueAsNumber(binding);
+ if (double(int(d)) == d)
+ return noError;
+ }
+ return warnOrError(tr("Invalid property assignment: int expected"));
+ }
+ break;
+ case QMetaType::Float: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return warnOrError(tr("Invalid property assignment: number expected"));
+ }
+ }
+ break;
+ case QVariant::Double: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return warnOrError(tr("Invalid property assignment: number expected"));
+ }
+ }
+ break;
+ case QVariant::Color: {
+ bool ok = false;
+ QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: color expected"));
+ }
+ }
+ break;
+#if QT_CONFIG(datestring)
+ case QVariant::Date: {
+ bool ok = false;
+ QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: date expected"));
+ }
+ }
+ break;
+ case QVariant::Time: {
+ bool ok = false;
+ QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: time expected"));
+ }
+ }
+ break;
+ case QVariant::DateTime: {
+ bool ok = false;
+ QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: datetime expected"));
+ }
+ }
+ break;
+#endif // datestring
+ case QVariant::Point: {
+ bool ok = false;
+ QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::PointF: {
+ bool ok = false;
+ QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::Size: {
+ bool ok = false;
+ QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: size expected"));
+ }
+ }
+ break;
+ case QVariant::SizeF: {
+ bool ok = false;
+ QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: size expected"));
+ }
+ }
+ break;
+ case QVariant::Rect: {
+ bool ok = false;
+ QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: rect expected"));
+ }
+ }
+ break;
+ case QVariant::RectF: {
+ bool ok = false;
+ QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
+ if (!ok) {
+ return warnOrError(tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::Bool: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ return warnOrError(tr("Invalid property assignment: boolean expected"));
+ }
+ }
+ break;
+ case QVariant::Vector2D: {
+ struct {
+ float xp;
+ float yp;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: 2D vector expected"));
+ }
+ }
+ break;
+ case QVariant::Vector3D: {
+ struct {
+ float xp;
+ float yp;
+ float zy;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: 3D vector expected"));
+ }
+ }
+ break;
+ case QVariant::Vector4D: {
+ struct {
+ float xp;
+ float yp;
+ float zy;
+ float wp;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: 4D vector expected"));
+ }
+ }
+ break;
+ case QVariant::Quaternion: {
+ struct {
+ float wp;
+ float xp;
+ float yp;
+ float zp;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: quaternion expected"));
+ }
+ }
+ break;
+ case QVariant::RegExp:
+ case QVariant::RegularExpression:
+ return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
+ default: {
+ // generate single literal value assignment to a list property if required
+ if (property->propType() == qMetaTypeId<QList<qreal> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return warnOrError(tr("Invalid property assignment: number or array of numbers expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<int> >()) {
+ bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
+ if (ok) {
+ double n = compilationUnit->bindingValueAsNumber(binding);
+ if (double(int(n)) != n)
+ ok = false;
+ }
+ if (!ok)
+ return warnOrError(tr("Invalid property assignment: int or array of ints expected"));
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<bool> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ return warnOrError(tr("Invalid property assignment: bool or array of bools expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return warnOrError(tr("Invalid property assignment: url or array of urls expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<QString> >()) {
+ if (!binding->evaluatesToString()) {
+ return warnOrError(tr("Invalid property assignment: string or array of strings expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QJSValue>()) {
+ break;
+ } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
+ break;
+ } else if (property->isQObject()
+ && binding->type == QV4::CompiledData::Binding::Type_Null) {
+ break;
+ }
+
+ // otherwise, try a custom type assignment
+ QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
+ if (!converter) {
+ return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType()))));
+ }
+ }
+ break;
+ }
+ return noError;
+}
+
+/*!
+ Returns true if from can be assigned to a (QObject) property of type
+ to.
+*/
+bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
+{
+ QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
+
+ while (fromMo) {
+ if (fromMo == toMo)
+ return true;
+ fromMo = fromMo->parent();
+ }
+ return false;
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
+{
+ QVector<QQmlCompileError> errors;
+ errors.append(QQmlCompileError(location, description));
+ return errors;
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const
+{
+ QVector<QQmlCompileError> errors;
+ errors.append(error);
+ return errors;
+}
+
+QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
+{
+ QQmlCompileError noError;
+
+ if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
+ Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
+
+ bool isValueSource = false;
+ bool isPropertyInterceptor = false;
+
+ const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex);
+ if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) {
+ QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ const QMetaObject *mo = cache->firstCppMetaObject();
+ QQmlType qmlType;
+ while (mo && !qmlType.isValid()) {
+ qmlType = QQmlMetaType::qmlType(mo);
+ mo = mo->superClass();
+ }
+ Q_ASSERT(qmlType.isValid());
+
+ isValueSource = qmlType.propertyValueSourceCast() != -1;
+ isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1;
+ }
+
+ if (!isValueSource && !isPropertyInterceptor) {
+ return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
+ }
+
+ return noError;
+ }
+
+ if (QQmlMetaType::isInterface(property->propType())) {
+ // Can only check at instantiation time if the created sub-object successfully casts to the
+ // target interface.
+ return noError;
+ } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) {
+ // We can convert everything to QVariant :)
+ return noError;
+ } else if (property->isQList()) {
+ const int listType = enginePrivate->listType(property->propType());
+ if (!QQmlMetaType::isInterface(listType)) {
+ QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex);
+ if (!canCoerce(listType, source)) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
+ }
+ }
+ return noError;
+ } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
+ return noError;
+ } else if (QQmlValueTypeFactory::isValueType(property->propType())) {
+ auto typeName = QMetaType::typeName(property->propType());
+ return QQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object")
+ .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>"))
+ .arg(propertyName));
+ } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
+ } else {
+ // We want to use the raw metaObject here as the raw metaobject is the
+ // actual property type before we applied any extensions that might
+ // effect the properties on the type, but don't effect assignability
+ // Using -1 for the minor version ensures that we get the raw metaObject.
+ QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1);
+
+ // Will be true if the assigned type inherits propertyMetaObject
+ bool isAssignable = false;
+ // Determine isAssignable value
+ if (propertyMetaObject) {
+ QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex);
+ while (c && !isAssignable) {
+ isAssignable |= c == propertyMetaObject;
+ c = c->parent();
+ }
+ }
+
+ if (!isAssignable) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
+ .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType()))));
+ }
+ }
+ return noError;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h
new file mode 100644
index 0000000000..8244b2df21
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyvalidator_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QQMLPROPERTYVALIDATOR_P_H
+#define QQMLPROPERTYVALIDATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmltypecompiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyValidator
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
+public:
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
+
+ QVector<QQmlCompileError> validate();
+
+private:
+ QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const;
+ QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const;
+ QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const;
+
+ bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
+
+ Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const;
+ Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const;
+ QString stringAt(int index) const { return compilationUnit->stringAt(index); }
+ QV4::ResolvedTypeReference *resolvedType(int id) const
+ {
+ return compilationUnit->resolvedType(id);
+ }
+
+ QQmlEnginePrivate *enginePrivate;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
+ const QQmlImports &imports;
+ const QV4::CompiledData::Unit *qmlUnit;
+ const QQmlPropertyCacheVector &propertyCaches;
+
+ QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYVALIDATOR_P_H
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
new file mode 100644
index 0000000000..66320b8db9
--- /dev/null
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -0,0 +1,1429 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qqmltypecompiler_p.h"
+
+#include <private/qqmlirbuilder_p.h>
+#include <private/qqmlobjectcreator_p.h>
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qqmlcomponent_p.h>
+#include <private/qqmlpropertyresolver_p.h>
+
+#define COMPILE_EXCEPTION(token, desc) \
+ { \
+ recordError((token)->location, desc); \
+ return false; \
+ }
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
+ QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+ : resolvedTypes(resolvedTypeCache)
+ , engine(engine)
+ , typeData(typeData)
+ , dependencyHasher(dependencyHasher)
+ , typeNameCache(typeNameCache)
+ , document(parsedQML)
+{
+}
+
+QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
+{
+ // Build property caches and VME meta object data
+
+ for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd();
+ it != end; ++it) {
+ QQmlCustomParser *customParser = (*it)->type.customParser();
+ if (customParser)
+ customParsers.insert(it.key(), customParser);
+ }
+
+ QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
+
+
+ {
+ QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings,
+ engine, this, imports());
+ QQmlCompileError error = propertyCacheBuilder.buildMetaObjects();
+ if (error.isSet()) {
+ recordError(error);
+ return nullptr;
+ }
+ }
+
+ {
+ QQmlDefaultPropertyMerger merger(this);
+ merger.mergeDefaultProperties();
+ }
+
+ {
+ SignalHandlerConverter converter(this);
+ if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations())
+ return nullptr;
+ }
+
+ {
+ QQmlEnumTypeResolver enumResolver(this);
+ if (!enumResolver.resolveEnumBindings())
+ return nullptr;
+ }
+
+ {
+ QQmlCustomParserScriptIndexer cpi(this);
+ cpi.annotateBindingsWithScriptStrings();
+ }
+
+ {
+ QQmlAliasAnnotator annotator(this);
+ annotator.annotateBindingsToAliases();
+ }
+
+ // 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);
+ }
+
+ {
+ QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this);
+ if (!deferredAndCustomParserBindingScanner.scanObject())
+ return nullptr;
+ }
+
+ if (!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
+ // without type optimizations as their scope is always entirely dynamic.
+ QQmlScriptStringScanner sss(this);
+ sss.scan();
+ }
+
+ document->jsModule.fileName = typeData->urlString();
+ document->jsModule.finalUrl = typeData->finalUrlString();
+ QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine,
+ document->program, &document->jsGenerator.stringTable, engine->v4engine()->illegalNames());
+ QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
+ if (!jsCodeGen.generateCodeForComponents())
+ return nullptr;
+
+ document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
+ }
+
+ // Generate QML compiled type data structures
+
+ QmlIR::QmlUnitGenerator qmlGenerator;
+ qmlGenerator.generate(*document, dependencyHasher);
+
+ 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()));
+
+ if (errors.isEmpty())
+ return compilationUnit;
+ else
+ return nullptr;
+}
+
+void QQmlTypeCompiler::recordError(QQmlError error)
+{
+ error.setUrl(url());
+ errors << error;
+}
+
+void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
+{
+ QQmlError error;
+ error.setLine(location.line);
+ error.setColumn(location.column);
+ error.setDescription(description);
+ error.setUrl(url());
+ errors << error;
+}
+
+void QQmlTypeCompiler::recordError(const QQmlCompileError &error)
+{
+ recordError(error.location, error.description);
+}
+
+QString QQmlTypeCompiler::stringAt(int idx) const
+{
+ return document->stringAt(idx);
+}
+
+int QQmlTypeCompiler::registerString(const QString &str)
+{
+ return document->jsGenerator.registerString(str);
+}
+
+int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
+{
+ return document->jsGenerator.registerConstant(v);
+}
+
+const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
+{
+ return document->javaScriptCompilationUnit.unitData();
+}
+
+const QQmlImports *QQmlTypeCompiler::imports() const
+{
+ return &typeData->imports();
+}
+
+QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
+{
+ return &document->objects;
+}
+
+void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
+{
+ m_propertyCaches = std::move(caches);
+ Q_ASSERT(m_propertyCaches.count() > 0);
+}
+
+const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
+{
+ return &m_propertyCaches;
+}
+
+QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
+{
+ return std::move(m_propertyCaches);
+}
+
+QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
+{
+ return document->jsParserEngine.pool();
+}
+
+QStringRef QQmlTypeCompiler::newStringRef(const QString &string)
+{
+ return document->jsParserEngine.newStringRef(string);
+}
+
+const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const
+{
+ return &document->jsGenerator.stringTable;
+}
+
+QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
+{
+ return object->bindingAsString(document, scriptIndex);
+}
+
+void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion)
+{
+ const quint32 moduleIdx = registerString(module);
+ const quint32 qualifierIdx = registerString(qualifier);
+
+ for (int i = 0, count = document->imports.count(); i < count; ++i) {
+ const QV4::CompiledData::Import *existingImport = document->imports.at(i);
+ if (existingImport->type == QV4::CompiledData::Import::ImportLibrary
+ && existingImport->uriIndex == moduleIdx
+ && existingImport->qualifierIndex == qualifierIdx)
+ return;
+ }
+ auto pool = memoryPool();
+ QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
+ import->type = QV4::CompiledData::Import::ImportLibrary;
+ import->majorVersion = majorVersion;
+ import->minorVersion = minorVersion;
+ import->uriIndex = moduleIdx;
+ import->qualifierIndex = qualifierIdx;
+ document->imports.append(import);
+}
+
+QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
+ : compiler(typeCompiler)
+{
+}
+
+
+
+SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , enginePrivate(typeCompiler->enginePrivate())
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , imports(typeCompiler->imports())
+ , customParsers(typeCompiler->customParserCache())
+ , illegalNames(typeCompiler->enginePrivate()->v4engine()->illegalNames())
+ , propertyCaches(typeCompiler->propertyCaches())
+{
+}
+
+bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations()
+{
+ for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) {
+ const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
+ QQmlPropertyCache *cache = propertyCaches->at(objectIndex);
+ if (!cache)
+ continue;
+ if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) {
+ if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers))
+ continue;
+ }
+ const QString elementName = stringAt(obj->inheritedTypeNameIndex);
+ if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache))
+ return false;
+ }
+ return true;
+}
+
+bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache)
+{
+ // map from signal name defined in qml itself to list of parameters
+ QHash<QString, QStringList> customSignals;
+
+ for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ QString propertyName = stringAt(binding->propertyNameIndex);
+ // Attached property?
+ if (binding->type == 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()) {
+ if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
+ if (type.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+ type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
+ }
+ }
+ }
+
+ const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate);
+ if (!attachedType)
+ COMPILE_EXCEPTION(binding, tr("Non-existent attached object"));
+ QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType);
+ if (!convertSignalHandlerExpressionsToFunctionDeclarations(attachedObj, propertyName, cache))
+ return false;
+ continue;
+ }
+
+ if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName))
+ continue;
+
+ QQmlPropertyResolver resolver(propertyCache);
+
+ Q_ASSERT(propertyName.startsWith(QLatin1String("on")));
+ propertyName.remove(0, 2);
+
+ // Note that the property name could start with any alpha or '_' or '$' character,
+ // so we need to do the lower-casing of the first alpha character.
+ for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) {
+ if (propertyName.at(firstAlphaIndex).isUpper()) {
+ propertyName[firstAlphaIndex] = propertyName.at(firstAlphaIndex).toLower();
+ break;
+ }
+ }
+
+ QList<QString> parameters;
+
+ bool notInRevision = false;
+ QQmlPropertyData *signal = resolver.signal(propertyName, &notInRevision);
+ if (signal) {
+ int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
+ sigIndex = propertyCache->originalClone(sigIndex);
+
+ bool unnamedParameter = false;
+
+ QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex);
+ for (int i = 0; i < parameterNames.count(); ++i) {
+ const QString param = QString::fromUtf8(parameterNames.at(i));
+ if (param.isEmpty())
+ unnamedParameter = true;
+ else if (unnamedParameter) {
+ COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter."));
+ } else if (illegalNames.contains(param)) {
+ COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param));
+ }
+ parameters += param;
+ }
+ } else {
+ if (notInRevision) {
+ // Try assinging it as a property later
+ if (resolver.property(propertyName, /*notInRevision ptr*/nullptr))
+ continue;
+
+ const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
+
+ auto *typeRef = resolvedType(obj->inheritedTypeNameIndex);
+ const QQmlType type = typeRef ? typeRef->type : QQmlType();
+ if (type.isValid()) {
+ COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion()));
+ } else {
+ COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName));
+ }
+ }
+
+ // Try to look up the signal parameter names in the object itself
+
+ // build cache if necessary
+ if (customSignals.isEmpty()) {
+ for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) {
+ const QString &signalName = stringAt(signal->nameIndex);
+ customSignals.insert(signalName, signal->parameterStringList(compiler->stringPool()));
+ }
+
+ for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) {
+ const QString propName = stringAt(property->nameIndex);
+ customSignals.insert(propName, QStringList());
+ }
+ }
+
+ QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName);
+ if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) {
+ QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed")));
+ entry = customSignals.constFind(alternateName);
+ }
+
+ if (entry == customSignals.constEnd()) {
+ // Can't find even a custom signal, then just don't do anything and try
+ // keeping the binding as a regular property assignment.
+ continue;
+ }
+
+ parameters = entry.value();
+ }
+
+ // 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;
+ continue;
+ }
+
+ if (binding->type != QV4::CompiledData::Binding::Type_Script) {
+ if (binding->type < 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"));
+ }
+ }
+
+ QQmlJS::MemoryPool *pool = compiler->memoryPool();
+
+ QQmlJS::AST::FormalParameterList *paramList = nullptr;
+ for (const QString &param : qAsConst(parameters)) {
+ QStringRef paramNameRef = compiler->newStringRef(param);
+
+ QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr);
+ paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b);
+ }
+
+ if (paramList)
+ paramList = paramList->finish(pool);
+
+ QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
+ QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr;
+ if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) {
+ if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) {
+ functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body);
+ functionDeclaration->functionToken = fe->functionToken;
+ functionDeclaration->identifierToken = fe->identifierToken;
+ functionDeclaration->lparenToken = fe->lparenToken;
+ functionDeclaration->rparenToken = fe->rparenToken;
+ functionDeclaration->lbraceToken = fe->lbraceToken;
+ functionDeclaration->rbraceToken = fe->rbraceToken;
+ }
+ }
+ if (!functionDeclaration) {
+ QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
+ QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement);
+ body = body->finish();
+
+ functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
+ functionDeclaration->lbraceToken = functionDeclaration->functionToken
+ = foe->node->firstSourceLocation();
+ functionDeclaration->rbraceToken = foe->node->lastSourceLocation();
+ }
+ foe->node = functionDeclaration;
+ binding->propertyNameIndex = compiler->registerString(propertyName);
+ binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression;
+ }
+ return true;
+}
+
+QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+ , imports(typeCompiler->imports())
+{
+}
+
+bool QQmlEnumTypeResolver::resolveEnumBindings()
+{
+ for (int i = 0; i < qmlObjects.count(); ++i) {
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
+ if (!propertyCache)
+ continue;
+ const QmlIR::Object *obj = qmlObjects.at(i);
+
+ 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)
+ continue;
+
+ 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)
+ continue;
+
+ if (!pd->isEnum() && pd->propType() != QMetaType::Int)
+ continue;
+
+ if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+struct StaticQtMetaObject : public QObject
+{
+ static const QMetaObject *get()
+ { return &staticQtMetaObject; }
+};
+
+bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject)
+{
+ if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) {
+ COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString()));
+ }
+ binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
+// binding->setNumberValueInternal((double)enumValue);
+ binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
+ return true;
+}
+
+bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding)
+{
+ bool isIntProp = (prop->propType() == 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)));
+
+ Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+ const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
+ if (!string.constData()->isUpper())
+ 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)
+ return true;
+
+ int dot2 = string.indexOf(QLatin1Char('.'), dot+1);
+ if (dot2 != -1 && dot2 != string.length()-1) {
+ if (!string.at(dot+1).isUpper())
+ return true;
+ if (string.indexOf(QLatin1Char('.'), dot2+1) != -1)
+ return true;
+ }
+
+ QHashedStringRef typeName(string.constData(), dot);
+ const bool isQtObject = (typeName == QLatin1String("Qt"));
+ const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(dot + 1, dot2 - dot - 1) : QStringRef());
+ // ### consider supporting scoped enums in Qt namespace
+ const QStringRef enumValue = string.midRef(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1);
+
+ if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here?
+ // Allow enum assignment to ints.
+ bool ok;
+ int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok);
+ if (ok) {
+ if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject))
+ return false;
+ }
+ return true;
+ }
+ QQmlType type;
+ imports->resolveType(typeName, &type, nullptr, nullptr, nullptr);
+
+ if (!type.isValid() && !isQtObject)
+ return true;
+
+ int value = 0;
+ bool ok = false;
+
+ auto *tr = resolvedType(obj->inheritedTypeNameIndex);
+ if (type.isValid() && tr && tr->type == type) {
+ // When these two match, we can short cut the search
+ QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
+ QMetaEnum menum = mprop.enumerator();
+ QByteArray enumName = enumValue.toUtf8();
+ if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8())
+ return true;
+
+ if (mprop.isFlagType()) {
+ value = menum.keysToValue(enumName.constData(), &ok);
+ } else {
+ value = menum.keyToValue(enumName.constData(), &ok);
+ }
+ } else {
+ // Otherwise we have to search the whole type
+ if (type.isValid()) {
+ if (!scopedEnumName.isEmpty())
+ value = type.scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok);
+ else
+ value = type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok);
+ } else {
+ QByteArray enumName = enumValue.toUtf8();
+ const QMetaObject *metaObject = StaticQtMetaObject::get();
+ for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
+ QMetaEnum e = metaObject->enumerator(ii);
+ value = e.keyToValue(enumName.constData(), &ok);
+ }
+ }
+ }
+
+ if (!ok)
+ return true;
+
+ return assignEnumToBinding(binding, enumValue, value, isQtObject);
+}
+
+int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const
+{
+ Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer");
+ *ok = false;
+
+ if (scope != QLatin1String("Qt")) {
+ QQmlType type;
+ imports->resolveType(scope, &type, nullptr, 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);
+ }
+
+ const QMetaObject *mo = StaticQtMetaObject::get();
+ int i = mo->enumeratorCount();
+ const QByteArray ba = enumValue.toUtf8();
+ while (i--) {
+ int v = mo->enumerator(i).keyToValue(ba.constData(), ok);
+ if (*ok)
+ return v;
+ }
+ return -1;
+}
+
+QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , customParsers(typeCompiler->customParserCache())
+{
+}
+
+void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings()
+{
+ scanObjectRecursively(/*root object*/0);
+}
+
+void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings)
+{
+ const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
+ 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) {
+ 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);
+ }
+}
+
+QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+{
+}
+
+void QQmlAliasAnnotator::annotateBindingsToAliases()
+{
+ for (int i = 0; i < qmlObjects.count(); ++i) {
+ QQmlPropertyCache *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();
+
+ 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;
+ if (pd && pd->isAlias())
+ binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
+ }
+ }
+}
+
+QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+{
+
+}
+
+void QQmlScriptStringScanner::scan()
+{
+ const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>();
+ for (int i = 0; i < qmlObjects.count(); ++i) {
+ QQmlPropertyCache *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();
+
+ for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ 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;
+ if (!pd || pd->propType() != scriptStringMetaType)
+ continue;
+
+ QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
+ binding->stringIndex = compiler->registerString(script);
+ }
+ }
+}
+
+QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , enginePrivate(typeCompiler->enginePrivate())
+ , pool(typeCompiler->memoryPool())
+ , qmlObjects(typeCompiler->qmlObjects())
+ , propertyCaches(std::move(typeCompiler->takePropertyCaches()))
+{
+}
+
+static bool isUsableComponent(const QMetaObject *metaObject)
+{
+ // 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;
+}
+
+void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache)
+{
+ 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;
+ if (tr->type.isValid())
+ firstMetaObject = tr->type.metaObject();
+ else if (tr->compilationUnit)
+ firstMetaObject = tr->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;
+
+ QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), pd->typeMinorVersion());
+ 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.majorVersion(), componentType.minorVersion());
+
+ QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
+ syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), compiler->registerString(QString()));
+ syntheticComponent->location = binding->valueLocation;
+ syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
+
+ if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) {
+ auto typeRef = new QV4::ResolvedTypeReference;
+ typeRef->type = componentType;
+ typeRef->majorVersion = componentType.majorVersion();
+ typeRef->minorVersion = componentType.minorVersion();
+ 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);
+ }
+}
+
+bool QQmlComponentAndAliasResolver::resolve()
+{
+ // 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);
+ 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(rootBinding->value.objectIndex))
+ return false;
+
+ component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
+
+ if (!resolveAliases(componentRoots.at(i)))
+ return false;
+ }
+
+ // Collect ids and aliases for root
+ _idToObjectIndex.clear();
+ _objectsWithAliases.clear();
+
+ collectIdsAndAliases(/*root object*/0);
+
+ QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0);
+ rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
+
+ if (!resolveAliases(/*root object*/0))
+ return false;
+
+ // 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);
+
+ return true;
+}
+
+bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
+{
+ 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;
+}
+
+bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
+{
+ if (_objectsWithAliases.isEmpty())
+ return true;
+
+ QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
+
+ bool atLeastOneAliasResolved;
+ do {
+ atLeastOneAliasResolved = false;
+ QVector<int> pendingObjects;
+
+ for (int objectIndex: qAsConst(_objectsWithAliases)) {
+
+ QQmlCompileError error;
+ const auto result = resolveAliasesInObject(objectIndex, &error);
+
+ if (error.isSet()) {
+ recordError(error);
+ return false;
+ }
+
+ if (result == AllAliasesResolved) {
+ QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
+ if (error.isSet()) {
+ 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, QQmlCompileError *error)
+{
+ const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ if (!obj->aliasCount())
+ return AllAliasesResolved;
+
+ int numResolvedAliases = 0;
+ bool seenUnresolvedAlias = false;
+
+ for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
+ if (alias->flags & QV4::CompiledData::Alias::Resolved)
+ continue;
+
+ seenUnresolvedAlias = true;
+
+ const int idIndex = alias->idIndex;
+ const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
+ if (targetObjectIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ break;
+ }
+
+ const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+ Q_ASSERT(targetObject->id >= 0);
+ alias->targetObjectId = targetObject->id;
+ alias->aliasToLocalAlias = false;
+
+ const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
+
+ QStringRef property;
+ QStringRef subProperty;
+
+ const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
+ if (propertySeparator != -1) {
+ property = aliasPropertyValue.leftRef(propertySeparator);
+ subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
+ } else
+ property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
+
+ QQmlPropertyIndex propIdx;
+
+ if (property.isEmpty()) {
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ } else {
+ QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
+ if (!targetCache) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ break;
+ }
+
+ QQmlPropertyResolver resolver(targetCache);
+
+ 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) {
+ aliasPointsToOtherAlias = true;
+ break;
+ }
+ }
+ if (aliasPointsToOtherAlias) {
+ if (targetObjectIndex == objectIndex) {
+ alias->localAliasIndex = localAliasIndex;
+ alias->aliasToLocalAlias = true;
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ ++numResolvedAliases;
+ continue;
+ }
+
+ // restore
+ alias->idIndex = idIndex;
+ // Try again later and resolve the target alias first.
+ break;
+ }
+ }
+
+ if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ break;
+ }
+
+ propIdx = QQmlPropertyIndex(targetProperty->coreIndex());
+
+ if (!subProperty.isEmpty()) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType());
+ if (!valueTypeMetaObject) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+
+ int valueTypeIndex =
+ valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ } else {
+ if (targetProperty->isQObject())
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ }
+ }
+
+ alias->encodedMetaPropertyIndex = propIdx.toEncoded();
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ numResolvedAliases++;
+ }
+
+ if (numResolvedAliases == 0)
+ return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved;
+
+ return SomeAliasesResolved;
+}
+
+QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+ , customParsers(typeCompiler->customParserCache())
+ , _seenObjectWithId(false)
+{
+}
+
+bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
+{
+ return scanObject(/*root object*/0);
+}
+
+bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
+{
+ QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ if (obj->idNameIndex != 0)
+ _seenObjectWithId = true;
+
+ if (obj->flags & QV4::CompiledData::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);
+ }
+
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
+ if (!propertyCache)
+ return true;
+
+ QString defaultPropertyName;
+ QQmlPropertyData *defaultProperty = nullptr;
+ if (obj->indexOfDefaultPropertyOrAlias != -1) {
+ QQmlPropertyCache *cache = propertyCache->parent();
+ defaultPropertyName = cache->defaultPropertyName();
+ defaultProperty = cache->defaultProperty();
+ } else {
+ defaultPropertyName = propertyCache->defaultPropertyName();
+ defaultProperty = propertyCache->defaultProperty();
+ }
+
+ QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
+
+ QQmlPropertyResolver propertyResolver(propertyCache);
+
+ QStringList deferredPropertyNames;
+ {
+ 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(','));
+ }
+ }
+
+ 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 (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ continue;
+ }
+ } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ binding->flags |= QV4::CompiledData::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);
+ }
+
+ bool seenSubObjectWithId = 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)
+ return false;
+ _seenObjectWithId |= seenSubObjectWithId;
+ }
+
+ if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
+ && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
+
+ binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
+ }
+
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ continue;
+
+ if (!pd) {
+ if (customParser) {
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ }
+ }
+ }
+
+ return true;
+}
+
+QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen)
+ : QQmlCompilePass(typeCompiler)
+ , customParsers(typeCompiler->customParserCache())
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+ , v4CodeGen(v4CodeGen)
+{
+}
+
+bool QQmlJSCodeGenerator::generateCodeForComponents()
+{
+ const QVector<quint32> &componentRoots = compiler->componentRoots();
+ for (int i = 0; i < componentRoots.count(); ++i) {
+ if (!compileComponent(componentRoots.at(i)))
+ return false;
+ }
+
+ return compileComponent(/*root object*/0);
+}
+
+bool QQmlJSCodeGenerator::compileComponent(int contextObject)
+{
+ const QmlIR::Object *obj = qmlObjects.at(contextObject);
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->bindingCount() == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
+ Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ contextObject = componentBinding->value.objectIndex;
+ }
+
+ if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
+ return false;
+
+ return true;
+}
+
+bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex)
+{
+ QmlIR::Object *object = qmlObjects.at(objectIndex);
+ if (object->flags & QV4::CompiledData::Object::IsComponent)
+ return true;
+
+ if (object->functionsAndExpressions->count > 0) {
+ QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
+ for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
+ functionsToCompile << *foe;
+ const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile);
+ const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors();
+ if (!jsErrors.isEmpty()) {
+ for (const QQmlError &e : jsErrors)
+ compiler->recordError(e);
+ return false;
+ }
+
+ QQmlJS::MemoryPool *pool = compiler->memoryPool();
+ object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices);
+ }
+
+ for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
+ if (binding->type < QV4::CompiledData::Binding::Type_Object)
+ continue;
+
+ int target = binding->value.objectIndex;
+ int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex;
+
+ if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope))
+ return false;
+ }
+
+ return true;
+}
+
+QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+{
+
+}
+
+void QQmlDefaultPropertyMerger::mergeDefaultProperties()
+{
+ for (int i = 0; i < qmlObjects.count(); ++i)
+ mergeDefaultProperties(i);
+}
+
+void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
+{
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
+ if (!propertyCache)
+ return;
+
+ QmlIR::Object *object = qmlObjects.at(objectIndex);
+
+ QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
+ QmlIR::Binding *bindingsToReinsert = nullptr;
+ QmlIR::Binding *tail = nullptr;
+
+ QmlIR::Binding *previousBinding = nullptr;
+ QmlIR::Binding *binding = object->firstBinding();
+ while (binding) {
+ if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) {
+ previousBinding = binding;
+ binding = binding->next;
+ continue;
+ }
+
+ QmlIR::Binding *toReinsert = binding;
+ binding = object->unlinkBinding(previousBinding, binding);
+
+ if (!tail) {
+ bindingsToReinsert = toReinsert;
+ tail = toReinsert;
+ } else {
+ tail->next = toReinsert;
+ tail = tail->next;
+ }
+ tail->next = nullptr;
+ }
+
+ binding = bindingsToReinsert;
+ while (binding) {
+ QmlIR::Binding *toReinsert = binding;
+ binding = binding->next;
+ object->insertSorted(toReinsert);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
new file mode 100644
index 0000000000..f588909c42
--- /dev/null
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -0,0 +1,347 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QQMLTYPECOMPILER_P_H
+#define QQMLTYPECOMPILER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <qqmlerror.h>
+#include <qhash.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmlirbuilder_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEnginePrivate;
+class QQmlError;
+class QQmlTypeData;
+class QQmlImports;
+
+namespace QmlIR {
+struct Document;
+}
+
+namespace QV4 {
+namespace CompiledData {
+struct QmlUnit;
+struct Location;
+}
+}
+
+struct QQmlTypeCompiler
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
+public:
+ QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document,
+ const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
+
+ // --- interface used by QQmlPropertyCacheCreator
+ typedef QmlIR::Object CompiledObject;
+ const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); }
+ int objectCount() const { return document->objects.count(); }
+ QString stringAt(int idx) const;
+ QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
+ QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); }
+ QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
+ // ---
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compile();
+
+ QList<QQmlError> compilationErrors() const { return errors; }
+ void recordError(QQmlError error);
+ void recordError(const QV4::CompiledData::Location &location, const QString &description);
+ void recordError(const QQmlCompileError &error);
+
+ int registerString(const QString &str);
+ int registerConstant(QV4::ReturnedValue v);
+
+ const QV4::CompiledData::Unit *qmlUnit() const;
+
+ QUrl url() const { return typeData->finalUrl(); }
+ QQmlEnginePrivate *enginePrivate() const { return engine; }
+ const QQmlImports *imports() const;
+ QVector<QmlIR::Object *> *qmlObjects() const;
+ void setPropertyCaches(QQmlPropertyCacheVector &&caches);
+ const QQmlPropertyCacheVector *propertyCaches() const;
+ QQmlPropertyCacheVector &&takePropertyCaches();
+ void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; }
+ const QVector<quint32> &componentRoots() const { return m_componentRoots; }
+ QQmlJS::MemoryPool *memoryPool();
+ QStringRef newStringRef(const QString &string);
+ const QV4::Compiler::StringTableGenerator *stringPool() const;
+
+ const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; }
+
+ QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const;
+
+ void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion);
+
+ QV4::ResolvedTypeReference *resolvedType(int id) const
+ {
+ return resolvedTypes->value(id);
+ }
+
+private:
+ QList<QQmlError> errors;
+ QQmlEnginePrivate *engine;
+ QQmlTypeData *typeData;
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ QmlIR::Document *document;
+ // index is string index of type name (use obj->inheritedTypeNameIndex)
+ QHash<int, QQmlCustomParser*> customParsers;
+
+ // index in first hash is component index, vector inside contains object indices of objects with id property
+ QVector<quint32> m_componentRoots;
+ QQmlPropertyCacheVector m_propertyCaches;
+};
+
+struct QQmlCompilePass
+{
+ QQmlCompilePass(QQmlTypeCompiler *typeCompiler);
+
+ QString stringAt(int idx) const { return compiler->stringAt(idx); }
+protected:
+ void recordError(const QV4::CompiledData::Location &location, const QString &description) const
+ { compiler->recordError(location, description); }
+ void recordError(const QQmlCompileError &error)
+ { compiler->recordError(error); }
+
+ QV4::ResolvedTypeReference *resolvedType(int id) const
+ { return compiler->resolvedType(id); }
+ bool containsResolvedType(int id) const
+ { return compiler->resolvedTypes->contains(id); }
+ QV4::ResolvedTypeReferenceMap::iterator insertResolvedType(
+ int id, QV4::ResolvedTypeReference *value)
+ { return compiler->resolvedTypes->insert(id, value); }
+
+ QQmlTypeCompiler *compiler;
+};
+
+// "Converts" signal expressions to full-fleged function declarations with
+// parameters taken from the signal declarations
+// It also updates the QV4::CompiledData::Binding objects to set the property name
+// to the final signal name (onTextChanged -> textChanged) and sets the IsSignalExpression flag.
+struct SignalHandlerConverter : public QQmlCompilePass
+{
+ Q_DECLARE_TR_FUNCTIONS(SignalHandlerConverter)
+public:
+ SignalHandlerConverter(QQmlTypeCompiler *typeCompiler);
+
+ bool convertSignalHandlerExpressionsToFunctionDeclarations();
+
+private:
+ bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache);
+
+ QQmlEnginePrivate *enginePrivate;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlImports *imports;
+ const QHash<int, QQmlCustomParser*> &customParsers;
+ const QSet<QString> &illegalNames;
+ const QQmlPropertyCacheVector * const propertyCaches;
+};
+
+// ### This will go away when the codegen resolves all enums to constant expressions
+// and we replace the constant expression with a literal binding instead of using
+// a script.
+class QQmlEnumTypeResolver : public QQmlCompilePass
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlEnumTypeResolver)
+public:
+ QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler);
+
+ bool resolveEnumBindings();
+
+private:
+ bool assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject);
+ bool assignEnumToBinding(QmlIR::Binding *binding, const QString &enumName, int enumValue, bool isQtObject)
+ {
+ return assignEnumToBinding(binding, QStringRef(&enumName), enumValue, isQtObject);
+ }
+ bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache,
+ const QQmlPropertyData *prop,
+ QmlIR::Binding *binding);
+ int evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const;
+
+
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
+ const QQmlImports *imports;
+};
+
+class QQmlCustomParserScriptIndexer: public QQmlCompilePass
+{
+public:
+ QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler);
+
+ void annotateBindingsWithScriptStrings();
+
+private:
+ void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false);
+
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QHash<int, QQmlCustomParser*> &customParsers;
+};
+
+// Annotate properties bound to aliases with a flag
+class QQmlAliasAnnotator : public QQmlCompilePass
+{
+public:
+ QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler);
+
+ void annotateBindingsToAliases();
+private:
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
+};
+
+class QQmlScriptStringScanner : public QQmlCompilePass
+{
+public:
+ QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler);
+
+ void scan();
+
+private:
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
+};
+
+class QQmlComponentAndAliasResolver : public QQmlCompilePass
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver)
+public:
+ QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler);
+
+ bool resolve();
+
+protected:
+ void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache);
+ bool collectIdsAndAliases(int objectIndex);
+ bool resolveAliases(int componentIndex);
+ void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
+
+ enum AliasResolutionResult {
+ NoAliasResolved,
+ SomeAliasesResolved,
+ AllAliasesResolved
+ };
+
+ AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error);
+
+ QQmlEnginePrivate *enginePrivate;
+ QQmlJS::MemoryPool *pool;
+
+ QVector<QmlIR::Object*> *qmlObjects;
+
+ // indices of the objects that are actually Component {}
+ QVector<quint32> componentRoots;
+
+ // Deliberate choice of map over hash here to ensure stable generated output.
+ QMap<int, int> _idToObjectIndex;
+ QVector<int> _objectsWithAliases;
+
+ QQmlPropertyCacheVector propertyCaches;
+};
+
+class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
+{
+public:
+ QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler);
+
+ bool scanObject();
+
+private:
+ bool scanObject(int objectIndex);
+
+ QVector<QmlIR::Object*> *qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
+ const QHash<int, QQmlCustomParser*> &customParsers;
+
+ bool _seenObjectWithId;
+};
+
+// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone.
+class QQmlJSCodeGenerator : public QQmlCompilePass
+{
+public:
+ QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen);
+
+ bool generateCodeForComponents();
+
+private:
+ bool compileComponent(int componentRoot);
+ bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
+
+ const QHash<int, QQmlCustomParser*> &customParsers;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
+ QmlIR::JSCodeGen * const v4CodeGen;
+};
+
+class QQmlDefaultPropertyMerger : public QQmlCompilePass
+{
+public:
+ QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler);
+
+ void mergeDefaultProperties();
+
+private:
+ void mergeDefaultProperties(int objectIndex);
+
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPECOMPILER_P_H