aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/ftw/qhashedstring.cpp19
-rw-r--r--src/qml/qml/ftw/qhashedstring_p.h1
-rw-r--r--src/qml/qml/qml.pri2
-rw-r--r--src/qml/qml/qqml.cpp220
-rw-r--r--src/qml/qml/qqml.h208
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp39
-rw-r--r--src/qml/qml/qqmlapplicationengine.h1
-rw-r--r--src/qml/qml/qqmlapplicationengine_p.h6
-rw-r--r--src/qml/qml/qqmlbinding.cpp17
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp5
-rw-r--r--src/qml/qml/qqmlcomponent.cpp148
-rw-r--r--src/qml/qml/qqmlcomponent.h27
-rw-r--r--src/qml/qml/qqmlcomponent_p.h8
-rw-r--r--src/qml/qml/qqmlcomponentattached_p.h5
-rw-r--r--src/qml/qml/qqmlcontext.cpp2
-rw-r--r--src/qml/qml/qqmlcontext_p.h11
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp40
-rw-r--r--src/qml/qml/qqmlcustomparser_p.h9
-rw-r--r--src/qml/qml/qqmldatablob.cpp15
-rw-r--r--src/qml/qml/qqmldatablob_p.h2
-rw-r--r--src/qml/qml/qqmlengine.cpp122
-rw-r--r--src/qml/qml/qqmlengine_p.h8
-rw-r--r--src/qml/qml/qqmlerror.cpp15
-rw-r--r--src/qml/qml/qqmlextensioninterface.h11
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp53
-rw-r--r--src/qml/qml/qqmlextensionplugin.h13
-rw-r--r--src/qml/qml/qqmlimport.cpp407
-rw-r--r--src/qml/qml/qqmlimport_p.h33
-rw-r--r--src/qml/qml/qqmlincubator.cpp93
-rw-r--r--src/qml/qml/qqmlincubator.h10
-rw-r--r--src/qml/qml/qqmlincubator_p.h7
-rw-r--r--src/qml/qml/qqmlinfo.cpp5
-rw-r--r--src/qml/qml/qqmlirloader.cpp13
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp5
-rw-r--r--src/qml/qml/qqmllist.cpp132
-rw-r--r--src/qml/qml/qqmllist.h129
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp82
-rw-r--r--src/qml/qml/qqmllocale.cpp32
-rw-r--r--src/qml/qml/qqmllocale_p.h16
-rw-r--r--src/qml/qml/qqmlloggingcategory_p.h3
-rw-r--r--src/qml/qml/qqmlmetatype.cpp186
-rw-r--r--src/qml/qml/qqmlmetatype_p.h24
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp10
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h12
-rw-r--r--src/qml/qml/qqmlmoduleregistration.cpp66
-rw-r--r--src/qml/qml/qqmlmoduleregistration.h61
-rw-r--r--src/qml/qml/qqmlnotifier.cpp4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp339
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h45
-rw-r--r--src/qml/qml/qqmlplatform.cpp2
-rw-r--r--src/qml/qml/qqmlprivate.h410
-rw-r--r--src/qml/qml/qqmlproperty.cpp48
-rw-r--r--src/qml/qml/qqmlproperty_p.h4
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp54
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h1
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp16
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h202
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h201
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp93
-rw-r--r--src/qml/qml/qqmlpropertyvalidator_p.h13
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp5
-rw-r--r--src/qml/qml/qqmltype.cpp120
-rw-r--r--src/qml/qml/qqmltype_p.h16
-rw-r--r--src/qml/qml/qqmltype_p_p.h19
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp46
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h18
-rw-r--r--src/qml/qml/qqmltypedata.cpp283
-rw-r--r--src/qml/qml/qqmltypedata_p.h24
-rw-r--r--src/qml/qml/qqmltypeloader.cpp198
-rw-r--r--src/qml/qml/qqmltypeloader_p.h2
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent.cpp9
-rw-r--r--src/qml/qml/qqmltypeloaderthread.cpp17
-rw-r--r--src/qml/qml/qqmltypeloaderthread_p.h5
-rw-r--r--src/qml/qml/qqmltypemodule.cpp8
-rw-r--r--src/qml/qml/qqmltypenamecache.cpp4
-rw-r--r--src/qml/qml/qqmltypenotavailable.cpp2
-rw-r--r--src/qml/qml/qqmltypenotavailable_p.h6
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp2
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp39
-rw-r--r--src/qml/qml/qqmlvaluetype_p.h13
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp98
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h37
-rw-r--r--src/qml/qml/qqmlvme_p.h43
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp160
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h2
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp42
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp261
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h2
88 files changed, 4035 insertions, 1211 deletions
diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp
index 7a8fdd0a14..6c58ab87f4 100644
--- a/src/qml/qml/ftw/qhashedstring.cpp
+++ b/src/qml/qml/ftw/qhashedstring.cpp
@@ -102,6 +102,25 @@ QHashedStringRef QHashedStringRef::mid(int offset, int length) const
(length == -1 || (offset + length) > m_length)?(m_length - offset):length);
}
+QVector<QHashedStringRef> QHashedStringRef::split(const QChar sep) const
+{
+ QVector<QHashedStringRef> ret;
+ auto curLength = 0;
+ auto curOffset = m_data;
+ for (int offset = 0; offset < m_length; ++offset) {
+ if (*(m_data + offset) == sep) {
+ ret.push_back({curOffset, curLength});
+ curOffset = m_data + offset + 1;
+ curLength = 0;
+ } else {
+ ++curLength;
+ }
+ }
+ if (curLength > 0)
+ ret.push_back({curOffset, curLength});
+ return ret;
+}
+
bool QHashedStringRef::endsWith(const QString &s) const
{
return s.length() < m_length &&
diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h
index b9f3f81219..a2e10ff143 100644
--- a/src/qml/qml/ftw/qhashedstring_p.h
+++ b/src/qml/qml/ftw/qhashedstring_p.h
@@ -125,6 +125,7 @@ public:
bool endsWith(const QString &) const;
int indexOf(const QChar &, int from=0) const;
QHashedStringRef mid(int, int) const;
+ QVector<QHashedStringRef> split(const QChar sep) const;
inline bool isEmpty() const;
inline int length() const;
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 2e9c6f3de6..a16f3d4167 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -3,6 +3,7 @@ SOURCES += \
$$PWD/qqmldatablob.cpp \
$$PWD/qqmldirdata.cpp \
$$PWD/qqmlerror.cpp \
+ $$PWD/qqmlmoduleregistration.cpp \
$$PWD/qqmlopenmetaobject.cpp \
$$PWD/qqmlscriptblob.cpp \
$$PWD/qqmlscriptdata.cpp \
@@ -71,6 +72,7 @@ HEADERS += \
$$PWD/qqmldatablob_p.h \
$$PWD/qqmldirdata_p.h \
$$PWD/qqmlglobal_p.h \
+ $$PWD/qqmlmoduleregistration.h \
$$PWD/qqmlopenmetaobject_p.h \
$$PWD/qqmlscriptblob_p.h \
$$PWD/qqmlscriptdata_p.h \
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 5b16a3c9e2..fe4e2f4e55 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -46,6 +46,7 @@
#include <private/qqmlmetatypedata_p.h>
#include <private/qqmltype_p_p.h>
#include <private/qqmltypemodule_p_p.h>
+#include <private/qqmltypenotavailable_p.h>
#include <QtCore/qmutex.h>
@@ -61,7 +62,7 @@ void qmlClearTypeRegistrations() // Declared in qqml.h
//From qqml.h
bool qmlProtectModule(const char *uri, int majVersion)
{
- return QQmlMetaType::protectModule(uri, majVersion);
+ return QQmlMetaType::protectModule(QString::fromUtf8(uri), majVersion);
}
//From qqml.h
@@ -103,33 +104,188 @@ QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJS
return m_object;
};
+static QVector<int> availableRevisions(const QMetaObject *metaObject)
+{
+ QVector<int> revisions;
+ if (!metaObject)
+ return revisions;
+ const int propertyOffset = metaObject->propertyOffset();
+ const int propertyCount = metaObject->propertyCount();
+ for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
+ propertyIndex < propertyEnd; ++propertyIndex) {
+ const QMetaProperty property = metaObject->property(propertyIndex);
+ if (int revision = property.revision())
+ revisions.append(revision);
+ }
+ const int methodOffset = metaObject->methodOffset();
+ const int methodCount = metaObject->methodCount();
+ for (int methodIndex = methodOffset, methodEnd = methodOffset + methodCount;
+ methodIndex < methodEnd; ++methodIndex) {
+ const QMetaMethod method = metaObject->method(methodIndex);
+ if (int revision = method.revision())
+ revisions.append(revision);
+ }
+
+ // Need to also check parent meta objects, as their revisions are inherited.
+ if (const QMetaObject *superMeta = metaObject->superClass())
+ revisions += availableRevisions(superMeta);
+
+ return revisions;
+}
+
/*
This method is "over generalized" to allow us to (potentially) register more types of things in
the future without adding exported symbols.
*/
int QQmlPrivate::qmlregister(RegistrationType type, void *data)
{
- if (type == AutoParentRegistration) {
+ QQmlType dtype;
+ switch (type) {
+ case AutoParentRegistration:
return QQmlMetaType::registerAutoParentFunction(
*reinterpret_cast<RegisterAutoParent *>(data));
- } else if (type == QmlUnitCacheHookRegistration) {
+ case QmlUnitCacheHookRegistration:
return QQmlMetaType::registerUnitCacheHook(
*reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
+ case TypeAndRevisionsRegistration: {
+ const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
+ const char *elementName = classElementName(type.classInfoMetaObject);
+ const bool creatable = (elementName != nullptr)
+ && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
+
+ const QString noCreateReason = creatable
+ ? QString()
+ : QString::fromUtf8(classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
+ RegisterType revisionRegistration = {
+ 1,
+ type.typeId,
+ type.listId,
+ creatable ? type.objectSize : 0,
+ nullptr,
+ noCreateReason,
+ type.uri,
+ type.versionMajor,
+ -1,
+ nullptr,
+ type.metaObject,
+ type.attachedPropertiesFunction,
+ type.attachedPropertiesMetaObject,
+ type.parserStatusCast,
+ type.valueSourceCast,
+ type.valueInterceptorCast,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ nullptr,
+ -1
+ };
+
+ const int added = intClassInfo(type.classInfoMetaObject, "QML.AddedInMinorVersion", 0);
+ const int removed = intClassInfo(type.classInfoMetaObject, "QML.RemovedInMinorVersion", -1);
+
+ auto revisions = availableRevisions(type.metaObject);
+ revisions.append(qMax(added, 0));
+ if (type.attachedPropertiesMetaObject)
+ revisions += availableRevisions(type.attachedPropertiesMetaObject);
+
+ std::sort(revisions.begin(), revisions.end());
+ const auto it = std::unique(revisions.begin(), revisions.end());
+ revisions.erase(it, revisions.end());
+
+ const bool typeWasRemoved = removed >= added;
+ for (int revision : revisions) {
+ if (revision < added)
+ continue;
+
+ // When removed, we still add revisions, but anonymous ones
+ if (typeWasRemoved && revision >= removed) {
+ revisionRegistration.elementName = nullptr;
+ revisionRegistration.create = nullptr;
+ } else {
+ revisionRegistration.elementName = elementName;
+ revisionRegistration.create = creatable ? type.create : nullptr;
+ }
+
+ // Equivalent of qmlRegisterRevision<T, revision>(...)
+ revisionRegistration.versionMinor = revision;
+ revisionRegistration.revision = revision;
+ revisionRegistration.customParser = type.customParserFactory();
+
+ qmlregister(TypeRegistration, &revisionRegistration);
+ }
+ break;
}
+ case SingletonAndRevisionsRegistration: {
+ const RegisterSingletonTypeAndRevisions &type
+ = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
+ const char *elementName = classElementName(type.classInfoMetaObject);
+ RegisterSingletonType revisionRegistration = {
+ QmlCurrentSingletonTypeRegistrationVersion,
+ type.uri,
+ type.versionMajor,
+ -1,
+ elementName,
- QQmlType dtype;
- if (type == TypeRegistration)
+ type.scriptApi,
+ nullptr,
+ type.instanceMetaObject,
+ type.typeId,
+ -1,
+
+ type.generalizedQobjectApi
+ };
+
+ const int added = intClassInfo(type.classInfoMetaObject, "QML.AddedInMinorVersion", 0);
+ const int removed = intClassInfo(type.classInfoMetaObject, "QML.RemovedInMinorVersion", -1);
+
+ auto revisions = availableRevisions(type.instanceMetaObject);
+ revisions.append(qMax(added, 0));
+
+ std::sort(revisions.begin(), revisions.end());
+ const auto it = std::unique(revisions.begin(), revisions.end());
+ revisions.erase(it, revisions.end());
+
+ const bool typeWasRemoved = removed >= added;
+ for (int revision : qAsConst(revisions)) {
+ if (revision < added)
+ continue;
+
+ // When removed, we still add revisions, but anonymous ones
+ if (typeWasRemoved && revision >= removed) {
+ revisionRegistration.typeName = nullptr;
+ revisionRegistration.scriptApi = nullptr;
+ revisionRegistration.generalizedQobjectApi = nullptr;
+ } else {
+ revisionRegistration.typeName = elementName;
+ revisionRegistration.scriptApi = type.scriptApi;
+ revisionRegistration.generalizedQobjectApi = type.generalizedQobjectApi;
+ }
+
+ // Equivalent of qmlRegisterRevision<T, revision>(...)
+ revisionRegistration.versionMinor = revision;
+ revisionRegistration.revision = revision;
+
+ qmlregister(SingletonRegistration, &revisionRegistration);
+ }
+ break;
+ }
+ case TypeRegistration:
dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data));
- else if (type == InterfaceRegistration)
+ break;
+ case InterfaceRegistration:
dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data));
- else if (type == SingletonRegistration)
+ break;
+ case SingletonRegistration:
dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
- else if (type == CompositeRegistration)
+ break;
+ case CompositeRegistration:
dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
- else if (type == CompositeSingletonRegistration)
+ break;
+ case CompositeSingletonRegistration:
dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
- else
+ break;
+ default:
return -1;
+ }
if (!dtype.isValid())
return -1;
@@ -155,6 +311,50 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
case CompositeSingletonRegistration:
QQmlMetaType::unregisterType(data);
break;
+ case TypeAndRevisionsRegistration:
+ case SingletonAndRevisionsRegistration:
+ // Currently unnecessary. We'd need a special data structure to hold
+ // URI + majorVersion and then we'd iterate the minor versions, look up the
+ // associated QQmlType objects by uri/elementName/major/minor and qmlunregister
+ // each of them.
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+namespace QQmlPrivate {
+ template<>
+ void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
+ const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject)
+ {
+ using T = QQmlTypeNotAvailable;
+
+ QML_GETTYPENAMES
+
+ RegisterTypeAndRevisions type = {
+ 0,
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
+ 0,
+ nullptr,
+
+ uri,
+ versionMajor,
+
+ &QQmlTypeNotAvailable::staticMetaObject,
+ classInfoMetaObject,
+
+ attachedPropertiesFunc<T>(),
+ attachedPropertiesMetaObject<T>(),
+
+ StaticCastSelector<T, QQmlParserStatus>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
+
+ nullptr, nullptr, qmlCreateCustomParser<T>
+ };
+
+ qmlregister(TypeAndRevisionsRegistration, &type);
}
}
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 8b7457d471..5ea1d17478 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -41,9 +41,6 @@
#define QQML_H
#include <QtQml/qqmlprivate.h>
-#include <QtQml/qqmlparserstatus.h>
-#include <QtQml/qqmlpropertyvaluesource.h>
-#include <QtQml/qqmllist.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qmetaobject.h>
@@ -51,6 +48,12 @@
#define QML_VERSION 0x020000
#define QML_VERSION_STR "2.0"
+#define QML_PRIVATE_NAMESPACE \
+ QT_PREPEND_NAMESPACE(QQmlPrivate)
+
+#define QML_REGISTER_TYPES_AND_REVISIONS \
+ QT_PREPEND_NAMESPACE(qmlRegisterTypesAndRevisions)
+
#define QML_DECLARE_TYPE(TYPE) \
Q_DECLARE_METATYPE(TYPE *) \
Q_DECLARE_METATYPE(QQmlListProperty<TYPE>)
@@ -64,6 +67,62 @@
#define QML_DECLARE_INTERFACE_HASMETATYPE(INTERFACE) \
QML_DECLARE_TYPE_HASMETATYPE(INTERFACE)
+#define QML_ELEMENT \
+ Q_CLASSINFO("QML.Element", "auto")
+
+#define QML_ANONYMOUS \
+ Q_CLASSINFO("QML.Element", "anonymous")
+
+#define QML_NAMED_ELEMENT(NAME) \
+ Q_CLASSINFO("QML.Element", #NAME)
+
+#define QML_UNCREATABLE(REASON) \
+ Q_CLASSINFO("QML.Creatable", "false") \
+ Q_CLASSINFO("QML.UncreatableReason", REASON)
+
+#define QML_SINGLETON \
+ Q_CLASSINFO("QML.Singleton", "true") \
+ enum class QmlIsSingleton {yes = true}; \
+ template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlSingleton; \
+ template<typename T, typename... Args> \
+ friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor);
+
+#define QML_ADDED_IN_MINOR_VERSION(VERSION) \
+ Q_CLASSINFO("QML.AddedInMinorVersion", #VERSION)
+
+#define QML_REMOVED_IN_MINOR_VERSION(VERSION) \
+ Q_CLASSINFO("QML.RemovedInMinorVersion", #VERSION)
+
+#define QML_ATTACHED(ATTACHED_TYPE) \
+ Q_CLASSINFO("QML.Attached", #ATTACHED_TYPE) \
+ using QmlAttachedType = ATTACHED_TYPE; \
+ template<class, class, bool> friend struct QML_PRIVATE_NAMESPACE::QmlAttached; \
+ template<class> friend struct QML_PRIVATE_NAMESPACE::QmlAttachedAccessor;
+
+#define QML_EXTENDED(EXTENDED_TYPE) \
+ Q_CLASSINFO("QML.Extended", #EXTENDED_TYPE) \
+ using QmlExtendedType = EXTENDED_TYPE; \
+ template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlExtended; \
+ template<typename T, typename... Args> \
+ friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor);
+
+#define QML_FOREIGN(FOREIGN_TYPE) \
+ Q_CLASSINFO("QML.Foreign", #FOREIGN_TYPE) \
+ using QmlForeignType = FOREIGN_TYPE; \
+ template<class, class> friend struct QML_PRIVATE_NAMESPACE::QmlResolved; \
+ template<typename T, typename... Args> \
+ friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor);
+
+#define QML_INTERFACE \
+ Q_CLASSINFO("QML.Element", "anonymous") \
+ enum class QmlIsInterface {yes = true}; \
+ template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlInterface; \
+ template<typename T, typename... Args> \
+ friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor);
+
+#define QML_UNAVAILABLE \
+ QML_FOREIGN(QQmlTypeNotAvailable)
+
enum { /* TYPEINFO flags */
QML_HAS_ATTACHED_PROPERTIES = 0x01
};
@@ -82,25 +141,11 @@ QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
-
-class QQmlPropertyValueInterceptor;
-
-#define QML_GETTYPENAMES \
- const char *className = T::staticMetaObject.className(); \
- const int nameLen = int(strlen(className)); \
- QVarLengthArray<char,48> pointerName(nameLen+2); \
- memcpy(pointerName.data(), className, size_t(nameLen)); \
- pointerName[nameLen] = '*'; \
- pointerName[nameLen+1] = '\0'; \
- const int listLen = int(strlen("QQmlListProperty<")); \
- QVarLengthArray<char,64> listName(listLen + nameLen + 2); \
- memcpy(listName.data(), "QQmlListProperty<", size_t(listLen)); \
- memcpy(listName.data()+listLen, className, size_t(nameLen)); \
- listName[listLen+nameLen] = '>'; \
- listName[listLen+nameLen+1] = '\0';
-
void Q_QML_EXPORT qmlClearTypeRegistrations();
+template<class T>
+QQmlCustomParser *qmlCreateCustomParser();
+
template<typename T>
int qmlRegisterAnonymousType(const char *uri, int versionMajor)
{
@@ -141,7 +186,8 @@ QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegi
}
#endif
-int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message);
+int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, const QString& message);
template<typename T>
int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
@@ -380,9 +426,8 @@ int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
-
template<typename T, typename E>
-int qmlRegisterExtendedType()
+int qmlRegisterExtendedType(const char *uri, int versionMajor)
{
QML_GETTYPENAMES
@@ -395,7 +440,7 @@ int qmlRegisterExtendedType()
nullptr,
QString(),
- nullptr, 0, 0, nullptr, &T::staticMetaObject,
+ uri, versionMajor, 0, nullptr, &T::staticMetaObject,
QQmlPrivate::attachedPropertiesFunc<T>(),
QQmlPrivate::attachedPropertiesMetaObject<T>(),
@@ -413,6 +458,15 @@ int qmlRegisterExtendedType()
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+#if QT_DEPRECATED_SINCE(5, 15)
+template<typename T, typename E>
+QT_DEPRECATED_VERSION_X_5_15("Use qmlRegisterExtendedType(uri, versionMajor) instead")
+int qmlRegisterExtendedType()
+{
+ return qmlRegisterExtendedType<T, E>("", 0);
+}
+#endif
+
template<typename T, typename E>
int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor,
const char *qmlName)
@@ -452,7 +506,9 @@ int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor,
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+#if QT_DEPRECATED_SINCE(5, 15)
template<typename T>
+QT_DEPRECATED_VERSION_X_5_15("Use qmlRegisterInterface(uri, versionMajor) instead")
int qmlRegisterInterface(const char *typeName)
{
QByteArray name(typeName);
@@ -461,12 +517,33 @@ int qmlRegisterInterface(const char *typeName)
QByteArray listName("QQmlListProperty<" + name + '>');
QQmlPrivate::RegisterInterface qmlInterface = {
- 0,
+ 1,
qRegisterNormalizedMetaType<T *>(pointerName.constData()),
qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
- qobject_interface_iid<T *>()
+ qobject_interface_iid<T *>(),
+ "",
+ 0
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::InterfaceRegistration, &qmlInterface);
+}
+#endif
+
+template<typename T>
+int qmlRegisterInterface(const char *uri, int versionMajor)
+{
+ QML_GETTYPENAMES
+
+ QQmlPrivate::RegisterInterface qmlInterface = {
+ 1,
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T>>(listName.constData()),
+ qobject_interface_iid<T *>(),
+
+ uri,
+ versionMajor
};
return QQmlPrivate::qmlregister(QQmlPrivate::InterfaceRegistration, &qmlInterface);
@@ -733,6 +810,85 @@ inline int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, i
return QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &type);
}
+template<class T, class Resolved, class Extended, bool Singleton, bool Interface>
+struct QmlTypeAndRevisionsRegistration;
+
+template<class T, class Resolved, class Extended>
+struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, false, false> {
+ static void registerTypeAndRevisions(const char *uri, int versionMajor)
+ {
+ QQmlPrivate::qmlRegisterTypeAndRevisions<Resolved, Extended>(
+ uri, versionMajor, &T::staticMetaObject);
+ }
+};
+
+template<class T, class Resolved>
+struct QmlTypeAndRevisionsRegistration<T, Resolved, void, true, false> {
+ static void registerTypeAndRevisions(const char *uri, int versionMajor)
+ {
+ QQmlPrivate::qmlRegisterSingletonAndRevisions<Resolved>(
+ uri, versionMajor, &T::staticMetaObject);
+ }
+};
+
+template<class T, class Resolved>
+struct QmlTypeAndRevisionsRegistration<T, Resolved, void, false, true> {
+ static void registerTypeAndRevisions(const char *uri, int versionMajor)
+ {
+ qmlRegisterInterface<Resolved>(uri, versionMajor);
+ }
+};
+
+template<typename T = void, typename... Args>
+void qmlRegisterTypesAndRevisions(const char *uri, int versionMajor);
+
+template<typename T, typename... Args>
+void qmlRegisterTypesAndRevisions(const char *uri, int versionMajor)
+{
+ QmlTypeAndRevisionsRegistration<
+ T, typename QQmlPrivate::QmlResolved<T>::Type,
+ typename QQmlPrivate::QmlExtended<T>::Type,
+ QQmlPrivate::QmlSingleton<T>::Value,
+ QQmlPrivate::QmlInterface<T>::Value>
+ ::registerTypeAndRevisions(uri, versionMajor);
+ qmlRegisterTypesAndRevisions<Args...>(uri, versionMajor);
+}
+
+template<>
+inline void qmlRegisterTypesAndRevisions<>(const char *, int) {}
+
+inline void qmlRegisterNamespaceAndRevisions(const QMetaObject *metaObject,
+ const char *uri, int versionMajor)
+{
+ QQmlPrivate::RegisterTypeAndRevisions type = {
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr,
+
+ uri,
+ versionMajor,
+
+ metaObject,
+ metaObject,
+
+ nullptr,
+ nullptr,
+
+ -1,
+ -1,
+ -1,
+
+ nullptr,
+ nullptr,
+
+ &qmlCreateCustomParser<void>
+ };
+
+ qmlregister(QQmlPrivate::TypeAndRevisionsRegistration, &type);
+}
+
int Q_QML_EXPORT qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 7f80fe5e1c..6ee03b39e5 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE
QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e)
: QQmlEnginePrivate(e)
{
+ uiLanguage = QLocale().bcp47Name();
}
QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate()
@@ -72,6 +73,7 @@ void QQmlApplicationEnginePrivate::init()
&QCoreApplication::quit, Qt::QueuedConnection);
q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(),
&QCoreApplication::exit, Qt::QueuedConnection);
+ q->connect(q, SIGNAL(uiLanguageChanged()), q_func(), SLOT(_q_loadTranslations()));
#if QT_CONFIG(translation)
QTranslator* qtTranslator = new QTranslator(q);
if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm")))
@@ -83,22 +85,27 @@ void QQmlApplicationEnginePrivate::init()
QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true));
}
-void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile)
+void QQmlApplicationEnginePrivate::_q_loadTranslations()
{
#if QT_CONFIG(translation)
- if (rootFile.scheme() != QLatin1String("file") && rootFile.scheme() != QLatin1String("qrc"))
+ if (translationsDirectory.isEmpty())
return;
- QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile));
-
Q_Q(QQmlApplicationEngine);
- QTranslator *translator = new QTranslator(q);
- if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm")))
- QCoreApplication::installTranslator(translator);
- else
- delete translator;
-#else
- Q_UNUSED(rootFile)
+
+ QScopedPointer<QTranslator> translator(new QTranslator);
+ if (!uiLanguage.isEmpty()) {
+ QLocale locale(uiLanguage);
+ if (translator->load(locale, QLatin1String("qml"), QLatin1String("_"), translationsDirectory, QLatin1String(".qm"))) {
+ if (activeTranslator)
+ QCoreApplication::removeTranslator(activeTranslator.data());
+ QCoreApplication::installTranslator(translator.data());
+ activeTranslator.swap(translator);
+ }
+ } else {
+ activeTranslator.reset();
+ }
+ q->retranslate();
#endif
}
@@ -106,7 +113,14 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &
{
Q_Q(QQmlApplicationEngine);
- loadTranslations(url); //Translations must be loaded before the QML file is
+ if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) {
+ QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ translationsDirectory = fi.path() + QLatin1String("/i18n");
+ } else {
+ translationsDirectory.clear();
+ }
+
+ _q_loadTranslations(); //Translations must be loaded before the QML file is
QQmlComponent *c = new QQmlComponent(q, q);
if (dataFlag)
@@ -181,6 +195,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
\list
\li Translation files must have "qml_" prefix e.g. qml_ja_JP.qm.
\endlist
+ \li Translations are reloaded when the \c QJSEngine::uiLanguage / \c Qt.uiLanguage property is changed.
\li Automatically sets an incubation controller if the scene contains a QQuickWindow.
\li Automatically sets a \c QQmlFileSelector as the url interceptor, applying file selectors to all
QML files and assets.
diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h
index 2b4de91154..37f75d5068 100644
--- a/src/qml/qml/qqmlapplicationengine.h
+++ b/src/qml/qml/qqmlapplicationengine.h
@@ -74,6 +74,7 @@ Q_SIGNALS:
private:
Q_DISABLE_COPY(QQmlApplicationEngine)
+ Q_PRIVATE_SLOT(d_func(), void _q_loadTranslations())
Q_DECLARE_PRIVATE(QQmlApplicationEngine)
};
diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h
index 1279e400e8..86f492510f 100644
--- a/src/qml/qml/qqmlapplicationengine_p.h
+++ b/src/qml/qml/qqmlapplicationengine_p.h
@@ -70,10 +70,14 @@ public:
void cleanUp();
void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false);
- void loadTranslations(const QUrl &rootFile);
+ void _q_loadTranslations();
void finishLoad(QQmlComponent *component);
QList<QObject *> objects;
QVariantMap initialProperties;
+ QString translationsDirectory;
+#if QT_CONFIG(translation)
+ QScopedPointer<QTranslator> activeTranslator;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 52572be2b2..b9566d5862 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -43,6 +43,10 @@
#include "qqmlcontext.h"
#include "qqmlinfo.h"
#include "qqmldata_p.h"
+
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+
#include <private/qqmlprofiler_p.h>
#include <private/qqmlexpression_p.h>
#include <private/qqmlscriptstring_p.h>
@@ -54,6 +58,8 @@
#include <private/qv4variantobject_p.h>
#include <private/qv4jscall_p.h>
+#include <qtqml_tracepoints_p.h>
+
#include <QVariant>
#include <QtCore/qdebug.h>
#include <QVector>
@@ -182,6 +188,8 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
if (canUseAccessor())
flags.setFlag(QQmlPropertyData::BypassInterceptor);
+ Q_TRACE_SCOPE(QQmlBinding, engine, function() ? function()->name()->toQString() : QString(),
+ sourceLocation().sourceFile, sourceLocation().line, sourceLocation().column);
QQmlBindingProfiler prof(QQmlEnginePrivate::get(engine)->profiler, function());
doUpdate(watcher, flags, scope);
@@ -316,7 +324,7 @@ protected:
break;
default:
if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
- if (vtw->d()->valueType->metaType.id() == pd->propType()) {
+ if (vtw->d()->valueType()->metaType.id() == pd->propType()) {
return vtw->write(m_target.data(), pd->coreIndex());
}
}
@@ -388,6 +396,11 @@ QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::Exe
b->QQmlJavaScriptExpression::setContext(ctxt);
b->setScopeObject(obj);
+ if (QQmlDebugTranslationService *service
+ = QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
+ service->foundTranslationBinding(b, obj, ctxt);
+ }
+
return b;
}
@@ -475,7 +488,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
if (!propertyMetaObject.isNull())
propertyType = propertyMetaObject.className();
}
- } else if (userType != QVariant::Invalid) {
+ } else if (userType != QMetaType::UnknownType) {
if (userType == QMetaType::Nullptr || userType == QMetaType::VoidStar)
valueType = "null";
else
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index ff01e737ca..b347bb3829 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -60,6 +60,7 @@
#include <QtCore/qdebug.h>
+#include <qtqml_tracepoints_p.h>
QT_BEGIN_NAMESPACE
@@ -352,6 +353,10 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
QQmlEngine *engine;
if (s->m_expression && (engine = s->m_expression->engine())) {
+ Q_TRACE_SCOPE(QQmlHandlingSignal, engine,
+ s->m_expression->function() ? s->m_expression->function()->name()->toQString() : QString(),
+ s->m_expression->sourceLocation().sourceFile, s->m_expression->sourceLocation().line,
+ s->m_expression->sourceLocation().column);
QQmlHandlingSignalProfiler prof(QQmlEnginePrivate::get(engine)->profiler, s->m_expression);
s->m_expression->evaluate(a);
if (s->m_expression && s->m_expression->hasError()) {
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index b26b90d2aa..9ee4fdbe41 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -50,6 +50,7 @@
#include "qqmlincubator.h"
#include "qqmlincubator_p.h"
#include <private/qqmljavascriptexpression_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4script_p.h>
@@ -248,9 +249,8 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
execute script code at startup, once the full QML environment has been
established.
- The corresponding handler is \c onCompleted. It can be declared on
- any object. The order of running the \c onCompleted handlers is
- undefined.
+ The \c onCompleted signal handler can be declared on any object. The order
+ of running the handlers is undefined.
\qml
Rectangle {
@@ -269,9 +269,8 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
work done in response to the \l {completed}{completed()} signal, or other
imperative code in your application.
- The corresponding handler is \c onDestruction. It can be declared on
- any object. The order of running the \c onDestruction handlers is
- undefined.
+ The \c onDestruction signal handler can be declared on any object. The
+ order of running the handlers is undefined.
\qml
Rectangle {
@@ -340,6 +339,16 @@ void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data
}
}
+RequiredProperties &QQmlComponentPrivate::requiredProperties()
+{
+ return state.creator->requiredProperties();
+}
+
+bool QQmlComponentPrivate::hadRequiredProperties() const
+{
+ return state.creator->componentHadRequiredProperties();
+}
+
void QQmlComponentPrivate::clear()
{
if (typeData) {
@@ -364,9 +373,9 @@ QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *cont
bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value)
{
- QQmlProperty prop(component, name);
- auto privProp = QQmlPropertyPrivate::get(prop);
- if (!prop.isValid() || !privProp->writeValueProperty(value, nullptr)) {
+ QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(component, name, requiredProperties());
+ QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
+ if (!prop.isValid() || !privProp->writeValueProperty(value, {})) {
QQmlError error{};
error.setUrl(url);
error.setDescription(QLatin1String("Could not set property %1").arg(name));
@@ -809,6 +818,10 @@ QObject *QQmlComponent::create(QQmlContext *context)
QObject *rv = d->doBeginCreate(this, context);
if (rv)
completeCreate();
+ if (rv && !d->requiredProperties().empty()) {
+ delete rv;
+ return nullptr;
+ }
return rv;
}
@@ -829,6 +842,10 @@ QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialPr
setInitialProperties(rv, initialProperties);
completeCreate();
}
+ if (!d->requiredProperties().empty()) {
+ d->requiredProperties().clear();
+ return nullptr;
+ }
return rv;
}
@@ -951,6 +968,7 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
if (!state->creator->populateDeferredProperties(object, deferredData))
state->errors << state->creator->errors;
+ deferredData->bindings.clear();
deferredState->constructionStates += state;
}
@@ -981,6 +999,57 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS
}
/*!
+ * \internal
+ * Finds the matching toplevel property with name \a name of the component \a createdComponent.
+ * If it was a required property or an alias to a required property contained in \a
+ * requiredProperties, it is removed from it.
+ *
+ * If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
+ * was found in requiredProperties.
+ *
+ * Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
+ * for further processing (for instance, actually setting the property value).
+ *
+ * Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
+ * classes which create components should not need it and should only need to call
+ * setInitialProperties.
+ */
+QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties &requiredProperties, bool* wasInRequiredProperties)
+{
+ QQmlProperty prop(createdComponent, name);
+ auto privProp = QQmlPropertyPrivate::get(prop);
+ if (prop.isValid()) {
+ // resolve outstanding required properties
+ auto targetProp = &privProp->core;
+ if (targetProp->isAlias()) {
+ auto target = createdComponent;
+ QQmlPropertyIndex originalIndex(targetProp->coreIndex());
+ QQmlPropertyIndex propIndex;
+ QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
+ QQmlData *data = QQmlData::get(target);
+ Q_ASSERT(data && data->propertyCache);
+ targetProp = data->propertyCache->property(propIndex.coreIndex());
+ } else {
+ // we need to get the pointer from the property cache instead of directly using
+ // targetProp else the lookup will fail
+ QQmlData *data = QQmlData::get(createdComponent);
+ Q_ASSERT(data && data->propertyCache);
+ targetProp = data->propertyCache->property(targetProp->coreIndex());
+ }
+ auto it = requiredProperties.find(targetProp);
+ if (it != requiredProperties.end()) {
+ if (wasInRequiredProperties)
+ *wasInRequiredProperties = true;
+ requiredProperties.erase(it);
+ } else {
+ if (wasInRequiredProperties)
+ *wasInRequiredProperties = false;
+ }
+ }
+ return prop;
+}
+
+/*!
This method provides advanced control over component instance creation.
In general, programmers should use QQmlComponent::create() to create a
component.
@@ -999,6 +1068,11 @@ void QQmlComponent::completeCreate()
void QQmlComponentPrivate::completeCreate()
{
+ const RequiredProperties& unsetRequiredProperties = requiredProperties();
+ for (const auto& unsetRequiredProperty: unsetRequiredProperties) {
+ QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
+ state.errors.push_back(error);
+ }
if (state.completePending) {
++creationDepth.localData();
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
@@ -1186,7 +1260,7 @@ struct QmlIncubatorObject : public QV4::Object
static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
void statusChanged(QQmlIncubator::Status);
- void setInitialState(QObject *);
+ void setInitialState(QObject *, RequiredProperties &requiredProperties);
};
}
@@ -1211,7 +1285,8 @@ public:
void setInitialState(QObject *o) override {
QV4::Scope scope(incubatorObject.engine());
QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
- i->setInitialState(o);
+ auto d = QQmlIncubatorPrivate::get(this);
+ i->setInitialState(o, d->requiredProperties());
}
QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating
@@ -1283,7 +1358,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
*/
-void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v)
+void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent)
{
QV4::Scope scope(engine);
QV4::ScopedObject object(scope);
@@ -1302,6 +1377,7 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
break;
object = o;
const QStringList properties = name->toQString().split(QLatin1Char('.'));
+ bool isTopLevelProperty = properties.size() == 1;
for (int i = 0; i < properties.length() - 1; ++i) {
name = engine->newString(properties.at(i));
object = object->get(name);
@@ -1318,12 +1394,40 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
if (engine->hasException) {
engine->hasException = false;
continue;
+ } else if (isTopLevelProperty) {
+ auto prop = removePropertyFromRequired(createdComponent, name->toQString(), requiredProperties);
}
}
engine->hasException = false;
}
+QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty)
+{
+ QQmlError error;
+ QString description = QLatin1String("Required property %1 was not initialized").arg(unsetRequiredProperty.propertyName);
+ switch (unsetRequiredProperty.aliasesToRequired.size()) {
+ case 0:
+ break;
+ case 1: {
+ const auto info = unsetRequiredProperty.aliasesToRequired.first();
+ description += QLatin1String("\nIt can be set via the alias property %1 from %2\n").arg(info.propertyName, info.fileUrl.toString());
+ break;
+ }
+ default:
+ description += QLatin1String("\nIt can be set via one of the following alias properties:");
+ for (auto aliasInfo: unsetRequiredProperty.aliasesToRequired) {
+ description += QLatin1String("\n- %1 (%2)").arg(aliasInfo.propertyName, aliasInfo.fileUrl.toString());
+ }
+ description += QLatin1Char('\n');
+ }
+ error.setDescription(description);
+ error.setUrl(unsetRequiredProperty.fileUrl);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(unsetRequiredProperty.location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(unsetRequiredProperty.location.column));
+ return error;
+}
+
/*!
\internal
*/
@@ -1371,7 +1475,17 @@ void QQmlComponent::createObject(QQmlV4Function *args)
if (!valuemap->isUndefined()) {
QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
- QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap);
+ QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap, d->requiredProperties(), rv);
+ }
+ if (!d->requiredProperties().empty()) {
+ QList<QQmlError> errors;
+ for (const auto &requiredProperty: d->requiredProperties()) {
+ errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(requiredProperty));
+ }
+ qmlWarning(rv, errors);
+ args->setReturnValue(QV4::Encode::null());
+ delete rv;
+ return;
}
d->completeCreate();
@@ -1503,7 +1617,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
}
// XXX used by QSGLoader
-void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate)
+void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties)
{
QV4::ExecutionEngine *v4engine = engine->handle();
QV4::Scope scope(v4engine);
@@ -1512,7 +1626,7 @@ void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext
Q_ASSERT(object->as<QV4::Object>());
if (!valuemap.isUndefined())
- setInitialProperties(v4engine, qmlContext, object, valuemap);
+ setInitialProperties(v4engine, qmlContext, object, valuemap, requiredProperties, toCreate);
}
QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
@@ -1602,7 +1716,7 @@ void QV4::Heap::QmlIncubatorObject::destroy() {
Object::destroy();
}
-void QV4::QmlIncubatorObject::setInitialState(QObject *o)
+void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties &requiredProperties)
{
QQmlComponent_setQmlParent(o, d()->parent);
@@ -1611,7 +1725,7 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o)
QV4::Scope scope(v4);
QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
- QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap);
+ QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap, requiredProperties, o);
}
}
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index f259c99b08..cb5d5a787c 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -70,6 +70,9 @@ class Q_QML_EXPORT QQmlComponent : public QObject
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(QUrl url READ url CONSTANT)
+ QML_NAMED_ELEMENT(Component)
+ QML_ATTACHED(QQmlComponentAttached)
+ Q_CLASSINFO("QML.Builtin", "QML")
public:
enum CompilationMode { PreferSynchronous, Asynchronous };
@@ -136,9 +139,29 @@ private:
friend class QQmlObjectCreator;
};
-QT_END_NAMESPACE
+// Don't do this at home.
+namespace QQmlPrivate {
+
+// Generally you cannot use QQmlComponentAttached as attached properties object in derived classes.
+// It is private.
+template<class T>
+struct OverridableAttachedType<T, QQmlComponentAttached>
+{
+ using Type = void;
+};
+
+// QQmlComponent itself is allowed to use QQmlComponentAttached, though.
+template<>
+struct OverridableAttachedType<QQmlComponent, QQmlComponentAttached>
+{
+ using Type = QQmlComponentAttached;
+};
+
+} // namespace QQmlPrivate
+
+
+QT_END_NAMESPACE
QML_DECLARE_TYPE(QQmlComponent)
-QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES)
#endif // QQMLCOMPONENT_H
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 2170646b89..a919eb45c0 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -86,8 +86,9 @@ public:
QObject *beginCreate(QQmlContextData *);
void completeCreate();
- void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate);
- static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v);
+ void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties);
+ static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent);
+ static QQmlError unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty);
virtual void incubateObject(
QQmlIncubator *incubationTask,
@@ -106,6 +107,8 @@ public:
qreal progress;
int start;
+ RequiredProperties& requiredProperties();
+ bool hadRequiredProperties() const;
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
struct ConstructionState {
@@ -134,6 +137,7 @@ public:
static void completeDeferred(QQmlEnginePrivate *enginePriv, DeferredState *deferredState);
static void complete(QQmlEnginePrivate *enginePriv, ConstructionState *state);
+ static QQmlProperty removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties& requiredProperties, bool *wasInRequiredProperties = nullptr);
QQmlEngine *engine;
QQmlGuardedContextData creationContext;
diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h
index e3bca18857..8ecd9da17d 100644
--- a/src/qml/qml/qqmlcomponentattached_p.h
+++ b/src/qml/qml/qqmlcomponentattached_p.h
@@ -61,6 +61,11 @@ QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlComponentAttached : public QObject
{
Q_OBJECT
+
+ // Used as attached object for QQmlComponent. We want qqmlcomponentattached_p.h to be #include'd
+ // when registering QQmlComponent, but we cannot #include it from qqmlcomponent.h. Therefore we
+ // force an anonymous type registration here.
+ QML_ANONYMOUS
public:
QQmlComponentAttached(QObject *parent = nullptr);
~QQmlComponentAttached();
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index f75a076bcb..9157bb95c3 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -553,7 +553,7 @@ QQmlContextData::QQmlContextData()
QQmlContextData::QQmlContextData(QQmlContext *ctxt)
: engine(nullptr), isInternal(false), isJSContext(false),
isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
- stronglyReferencedByParent(false), publicContext(ctxt), incubator(nullptr), componentObjectIndex(-1),
+ stronglyReferencedByParent(false), hasExtraObject(false), publicContext(ctxt), incubator(nullptr), componentObjectIndex(-1),
contextObject(nullptr), nextChild(nullptr), prevChild(nullptr),
expressions(nullptr), contextObjects(nullptr), idValues(nullptr), idValueCount(0),
componentAttached(nullptr)
diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h
index 5f7316b00c..f93393a11b 100644
--- a/src/qml/qml/qqmlcontext_p.h
+++ b/src/qml/qml/qqmlcontext_p.h
@@ -147,11 +147,16 @@ public:
quint32 hasEmittedDestruction:1;
quint32 isRootObjectInCreation:1;
quint32 stronglyReferencedByParent:1;
- quint32 dummy:25;
+ quint32 hasExtraObject:1; // used in QQmlDelegateModelItem::dataForObject to find the corresponding QQmlDelegateModelItem of an object
+ quint32 dummy:24;
QQmlContext *publicContext;
- // The incubator that is constructing this context if any
- QQmlIncubatorPrivate *incubator;
+ union {
+ // The incubator that is constructing this context if any
+ QQmlIncubatorPrivate *incubator;
+ // a pointer to extra data, currently only used in QQmlDelegateModel
+ QObject *extraObject;
+ };
// Compilation unit for contexts that belong to a compiled type.
QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit;
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index a5f34dafdf..5e6e1187e2 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -40,6 +40,7 @@
#include "qqmlcustomparser_p.h"
#include <private/qv4compileddata_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <QtCore/qdebug.h>
@@ -100,10 +101,10 @@ void QQmlCustomParser::clearErrors()
*/
void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description)
{
- QQmlJS::DiagnosticMessage error;
- error.line = location.line;
- error.column = location.column;
- error.message = description;
+ QQmlError error;
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setDescription(description);
exceptions << error;
}
@@ -131,8 +132,13 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const
// * <TypeName>.<EnumValue>
// * <TypeName>.<ScopedEnumName>.<EnumValue>
- int dot = script.indexOf('.');
- if (dot == -1 || dot == script.length()-1)
+ auto nextDot = [&](int dot) {
+ const int nextDot = script.indexOf('.', dot + 1);
+ return (nextDot == script.length() - 1) ? -1 : nextDot;
+ };
+
+ int dot = nextDot(-1);
+ if (dot == -1)
return -1;
QString scope = QString::fromUtf8(script.left(dot));
@@ -143,18 +149,32 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const
QQmlType type;
if (imports.isT1()) {
- imports.asT1()->resolveType(scope, &type, nullptr, nullptr, nullptr);
+ QQmlImportNamespace *ns = nullptr;
+ if (!imports.asT1()->resolveType(scope, &type, nullptr, nullptr, &ns))
+ return -1;
+ if (!type.isValid() && ns != nullptr) {
+ dot = nextDot(dot);
+ if (dot == -1 || !imports.asT1()->resolveType(QString::fromUtf8(script.left(dot)),
+ &type, nullptr, nullptr, nullptr)) {
+ return -1;
+ }
+ }
} else {
QQmlTypeNameCache::Result result = imports.asT2()->query(scope);
- if (result.isValid())
+ if (result.isValid()) {
type = result.type;
+ } else if (result.importNamespace) {
+ dot = nextDot(dot);
+ if (dot != -1)
+ type = imports.asT2()->query(QString::fromUtf8(script.left(dot))).type;
+ }
}
if (!type.isValid())
return -1;
- int dot2 = script.indexOf('.', dot+1);
- const bool dot2Valid = dot2 != -1 && dot2 != script.length()-1;
+ const int dot2 = nextDot(dot);
+ const bool dot2Valid = (dot2 != -1);
QByteArray enumValue = script.mid(dot2Valid ? dot2 + 1 : dot + 1);
QByteArray scopedEnumName = (dot2Valid ? script.mid(dot + 1, dot2 - dot - 1) : QByteArray());
if (!scopedEnumName.isEmpty())
diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h
index df8cbc9072..4eb709228e 100644
--- a/src/qml/qml/qqmlcustomparser_p.h
+++ b/src/qml/qml/qqmlcustomparser_p.h
@@ -83,7 +83,7 @@ public:
virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
- QVector<QQmlJS::DiagnosticMessage> errors() const { return exceptions; }
+ QVector<QQmlError> errors() const { return exceptions; }
protected:
void error(const QV4::CompiledData::Binding *binding, const QString& description)
@@ -97,7 +97,7 @@ protected:
const QMetaObject *resolveType(const QString&) const;
private:
- QVector<QQmlJS::DiagnosticMessage> exceptions;
+ QVector<QQmlError> exceptions;
QQmlEnginePrivate *engine;
const QQmlPropertyValidator *validator;
Flags m_flags;
@@ -107,11 +107,6 @@ private:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlCustomParser::Flags)
-#if 0
-#define QML_REGISTER_CUSTOM_TYPE(URI, VERSION_MAJ, VERSION_MIN, NAME, TYPE, CUSTOMTYPE) \
- qmlRegisterCustomType<TYPE>(#URI, VERSION_MAJ, VERSION_MIN, #NAME, #TYPE, new CUSTOMTYPE)
-#endif
-
QT_END_NAMESPACE
#endif
diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp
index 750fc6de50..b22e46b69c 100644
--- a/src/qml/qml/qqmldatablob.cpp
+++ b/src/qml/qml/qqmldatablob.cpp
@@ -42,9 +42,12 @@
#include <private/qqmlprofiler_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmltypeloaderthread_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <QtQml/qqmlengine.h>
+#include <qtqml_tracepoints_p.h>
+
#ifdef DATABLOB_DEBUG
#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false)
#else
@@ -304,22 +307,19 @@ void QQmlDataBlob::setError(const QList<QQmlError> &errors)
void QQmlDataBlob::setError(const QQmlJS::DiagnosticMessage &error)
{
QQmlError e;
- e.setColumn(error.column);
- e.setLine(error.line);
+ e.setColumn(qmlConvertSourceCoordinate<quint32, int>(error.loc.startColumn));
+ e.setLine(qmlConvertSourceCoordinate<quint32, int>(error.loc.startLine));
e.setDescription(error.message);
e.setUrl(url());
setError(e);
}
-void QQmlDataBlob::setError(const QVector<QQmlJS::DiagnosticMessage> &errors)
+void QQmlDataBlob::setError(const QVector<QQmlError> &errors)
{
QList<QQmlError> finalErrors;
finalErrors.reserve(errors.count());
for (const auto &error : errors) {
- QQmlError e;
- e.setColumn(error.column);
- e.setLine(error.line);
- e.setDescription(error.message);
+ QQmlError e = error;
e.setUrl(url());
finalErrors << e;
}
@@ -559,6 +559,7 @@ void QQmlDataBlob::notifyAllWaitingOnMe()
void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
{
Q_ASSERT(blob->status() == Error || blob->status() == Complete);
+ Q_TRACE_SCOPE(QQmlCompiling, blob->url());
QQmlCompilingProfiler prof(typeLoader()->profiler(), blob);
m_inCallback = true;
diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h
index 0450e94c02..cc066d29a1 100644
--- a/src/qml/qml/qqmldatablob_p.h
+++ b/src/qml/qml/qqmldatablob_p.h
@@ -132,7 +132,7 @@ protected:
void setError(const QQmlError &);
void setError(const QList<QQmlError> &errors);
void setError(const QQmlJS::DiagnosticMessage &error);
- void setError(const QVector<QQmlJS::DiagnosticMessage> &errors);
+ void setError(const QVector<QQmlError> &errors);
void setError(const QString &description);
void addDependency(QQmlDataBlob *);
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 93964ac76f..872a448bfd 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -57,6 +57,7 @@
#include "qqmlnotifier_p.h"
#include "qqmlincubator.h"
#include "qqmlabstracturlinterceptor.h"
+#include "qqmlsourcecoordinate_p.h"
#include <private/qqmldirparser_p.h>
#include <private/qqmlboundsignal_p.h>
#include <private/qqmljsdiagnosticmessage_p.h>
@@ -101,8 +102,6 @@
# endif
#endif // Q_OS_WIN
-Q_DECLARE_METATYPE(QQmlProperty)
-
QT_BEGIN_NAMESPACE
// Declared in qqml.h
@@ -197,37 +196,11 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
bool QQmlEnginePrivate::qml_debugging_enabled = false;
bool QQmlEnginePrivate::s_designerMode = false;
-void QQmlEnginePrivate::defineModule()
-{
- const char uri[] = "QtQml";
-
- qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component");
- qmlRegisterType<QObject>(uri, 2, 0, "QtObject");
- qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding");
- qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); // Only available in >= 2.8
- qmlRegisterType<QQmlBind, 14>(uri, 2, 14, "Binding");
-
- // TODO: We won't need Connections to be a custom type anymore once we can drop the
- // automatic signal handler inference from undeclared properties.
- qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
- qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 3, "Connections", new QQmlConnectionsParser); // Only available in QtQml >= 2.3
-
-#if QT_CONFIG(qml_animation)
- qmlRegisterType<QQmlTimer>(uri, 2, 0, "Timer");
-#endif
-
- qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); // Only available in >= 2.8
- qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory"); // Only available in >= 2.12
-
-#if QT_CONFIG(qml_locale)
- qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
-#endif
-}
-
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void QQmlEnginePrivate::registerQuickTypes()
{
// Don't add anything here. These are only for backwards compatibility.
+ // Also, don't use qmlRegisterTypesAndRevisions as that will auto-add future revisions.
const char uri[] = "QtQuick";
@@ -236,13 +209,17 @@ void QQmlEnginePrivate::registerQuickTypes()
qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding");
qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding");
qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
+
+ // Connections revision 3 was added in QtQml 2.3, but only in QtQuick 2.7.
qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 7, "Connections", new QQmlConnectionsParser);
+
#if QT_CONFIG(qml_animation)
qmlRegisterType<QQmlTimer>(uri, 2, 0,"Timer");
#endif
qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory");
qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory");
#if QT_CONFIG(qml_locale)
+ // Locale was added in QtQuick 2.0 and in QtQml 2.2
qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
#endif
}
@@ -445,6 +422,7 @@ The following functions are also on the Qt object.
\li \c "unix" - Other Unix-based OS
\li \c "windows" - Windows
\li \c "winrt" - WinRT / UWP
+ \li \c "wasm" - WebAssembly
\endlist
\row
@@ -707,11 +685,7 @@ QQmlEnginePrivate::~QQmlEnginePrivate()
for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) {
iter.value()->isRegisteredWithEngine = false;
-
- // since unregisterInternalCompositeType() will not be called in this
- // case, we have to clean up the type registration manually
- QMetaType::unregisterType(iter.value()->metaTypeId);
- QMetaType::unregisterType(iter.value()->listMetaTypeId);
+ QQmlMetaType::unregisterInternalCompositeType({iter.value()->metaTypeId, iter.value()->listMetaTypeId});
}
#if QT_CONFIG(qml_debug)
delete profiler;
@@ -815,10 +789,12 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (ddata->notifyList &&
- QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.loadRelaxed()) {
+ if (!ddata->notifyList)
+ return;
- if (!QObjectPrivate::get(object)->threadData->thread.loadAcquire())
+ auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
+ if (QThread::currentThreadId() != objectThreadData->threadId.loadRelaxed()) {
+ if (!objectThreadData->thread.loadAcquire())
return;
QMetaMethod m = QMetaObjectPrivate::signal(object->metaObject(), index);
@@ -850,7 +826,7 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject;
mpo->target = object;
- mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread.loadAcquire());
+ mpo->moveToThread(objectThreadData->thread.loadAcquire());
QCoreApplication::postEvent(mpo, ev.take());
} else {
@@ -1043,7 +1019,7 @@ QQmlEngine::~QQmlEngine()
// we do this here and not in the private dtor since otherwise a crash can
// occur (if we are the QObject parent of the QObject singleton instance)
// XXX TODO: performance -- store list of singleton types separately?
- QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes();
+ const QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes();
for (const QQmlType &currType : singletonTypes)
d->destroySingletonInstance(currType);
@@ -1358,6 +1334,21 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
}
/*!
+ \qmlproperty string Qt::uiLanguage
+ \since 5.15
+
+ The uiLanguage holds the name of the language to be used for user interface
+ string translations. It is exposed in C++ as QQmlEngine::uiLanguage property.
+
+ You can set the value freely and use it in bindings. It is recommended to set it
+ after installing translators in your application. By convention, an empty string
+ means no translation from the language used in the source code is intended to occur.
+
+ If you're using QQmlApplicationEngine and the value changes, QQmlEngine::retranslate()
+ will be called.
+*/
+
+/*!
\fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId)
Returns the instance of a singleton type that was registered under \a qmlTypeId.
@@ -1371,12 +1362,16 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
\code
class MySingleton : public QObject {
Q_OBJECT
+
+ // Register as default constructed singleton.
+ QML_ELEMENT
+ QML_SINGLETON
+
static int typeId;
// ...
};
- // Register with QObject* callback
- MySingleton::typeId = qmlRegisterSingletonType<MySingleton>(...);
+ MySingleton::typeId = qmlTypeId(...);
// Retrieve as QObject*
QQmlEngine engine;
@@ -1393,11 +1388,10 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
QJSValue instance = engine.singletonInstance<QJSValue>(typeId);
\endcode
- It is recommended to store the QML type id during registration, e.g. as a static member
- in the singleton class. Otherwise, a costly lookup via qmlTypeId() has to be performed
- at run-time.
+ It is recommended to store the QML type id, e.g. as a static member in the
+ singleton class. The lookup via qmlTypeId() is costly.
- \sa qmlRegisterSingletonType(), qmlTypeId()
+ \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
\since 5.12
*/
template<>
@@ -2132,15 +2126,15 @@ QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(
QList<QQmlError> errors;
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
if (m.isWarning()) {
- qWarning("%s:%d : %s", qPrintable(fileName), m.line, qPrintable(m.message));
+ qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
continue;
}
QQmlError error;
error.setUrl(QUrl(fileName));
error.setDescription(m.message);
- error.setLine(m.line);
- error.setColumn(m.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(m.loc.startLine));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(m.loc.startColumn));
errors << error;
}
return errors;
@@ -2275,7 +2269,7 @@ void QQmlEngine::setPluginPathList(const QStringList &paths)
On failure and if non-null, the \a errors list will have any errors which occurred prepended to it.
- The plugin has to be a Qt plugin which implements the QQmlExtensionPlugin interface.
+ The plugin has to be a Qt plugin which implements the QQmlEngineExtensionPlugin interface.
*/
bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
{
@@ -2390,12 +2384,23 @@ int QQmlEnginePrivate::listType(int t) const
return QQmlMetaType::listType(t);
}
+
+static QQmlPropertyCache *propertyCacheForPotentialInlineComponentType(int t, const QHash<int, QV4::ExecutableCompilationUnit *>::const_iterator &iter) {
+ if (t != (*iter)->metaTypeId) {
+ // this is an inline component, and what we have in the iterator is currently the parent compilation unit
+ for (auto &&icDatum: (*iter)->inlineComponentData)
+ if (icDatum.typeIds.id == t)
+ return (*iter)->propertyCaches.at(icDatum.objectIndex);
+ }
+ return (*iter)->rootPropertyCache().data();
+}
+
QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
{
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache().data());
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.baseMetaObject());
@@ -2407,7 +2412,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache().data());
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.metaObject());
@@ -2419,7 +2424,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache().data();
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2432,7 +2437,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache().data();
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2452,6 +2457,9 @@ void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilation
// The QQmlCompiledData is not referenced here, but it is removed from this
// hash in the QQmlCompiledData destructor
m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit);
+ for (auto &&data: compilationUnit->inlineComponentData) {
+ m_compositeTypes.insert(data.typeIds.id, compilationUnit);
+ }
}
void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
@@ -2460,6 +2468,14 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilati
Locker locker(this);
m_compositeTypes.remove(compilationUnit->metaTypeId);
+ for (auto&& icDatum: compilationUnit->inlineComponentData)
+ m_compositeTypes.remove(icDatum.typeIds.id);
+}
+
+QV4::ExecutableCompilationUnit *QQmlEnginePrivate::obtainExecutableCompilationUnit(int typeId)
+{
+ Locker locker(this);
+ return m_compositeTypes.value(typeId, nullptr);
}
template<>
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 502e2ed3d9..263c69e2d8 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -101,6 +101,13 @@ class QQmlProfiler;
class QQmlPropertyCapture;
class QQmlMetaObject;
+struct QObjectForeign {
+ Q_GADGET
+ QML_FOREIGN(QObject)
+ QML_NAMED_ELEMENT(QtObject)
+ Q_CLASSINFO("QML.Root", "QML")
+};
+
// This needs to be declared here so that the pool for it can live in QQmlEnginePrivate.
// The inline method definitions are in qqmljavascriptexpression_p.h
class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint
@@ -224,6 +231,7 @@ public:
QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1);
void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
+ QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(int typeId);
bool isTypeLoaded(const QUrl &url) const;
bool isScriptLoaded(const QUrl &url) const;
diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp
index 0b94ed3b49..da585d2126 100644
--- a/src/qml/qml/qqmlerror.cpp
+++ b/src/qml/qml/qqmlerror.cpp
@@ -77,12 +77,15 @@ QT_BEGIN_NAMESPACE
\sa QQuickView::errors(), QQmlComponent::errors()
*/
-class QQmlErrorPrivate : public QQmlJS::DiagnosticMessage
+class QQmlErrorPrivate
{
public:
- QQmlErrorPrivate() { type = QtWarningMsg; }
QUrl url;
QPointer<QObject> object;
+ QString message;
+ QtMsgType type = QtWarningMsg;
+ int line = -1;
+ int column = -1;
};
/*!
@@ -185,7 +188,7 @@ void QQmlError::setDescription(const QString &description)
int QQmlError::line() const
{
if (d)
- return qmlConvertSourceCoordinate<quint32, int>(d->line);
+ return d->line;
return -1;
}
@@ -196,7 +199,7 @@ void QQmlError::setLine(int line)
{
if (!d)
d = new QQmlErrorPrivate;
- d->line = qmlConvertSourceCoordinate<int, quint32>(line);
+ d->line = line;
}
/*!
@@ -205,7 +208,7 @@ void QQmlError::setLine(int line)
int QQmlError::column() const
{
if (d)
- return qmlConvertSourceCoordinate<quint32, int>(d->column);
+ return d->column;
return -1;
}
@@ -216,7 +219,7 @@ void QQmlError::setColumn(int column)
{
if (!d)
d = new QQmlErrorPrivate;
- d->column = qmlConvertSourceCoordinate<int, quint32>(column);
+ d->column = column;
}
/*!
diff --git a/src/qml/qml/qqmlextensioninterface.h b/src/qml/qml/qqmlextensioninterface.h
index d2eb79c5c9..1490bc512e 100644
--- a/src/qml/qml/qqmlextensioninterface.h
+++ b/src/qml/qml/qqmlextensioninterface.h
@@ -62,8 +62,14 @@ public:
virtual void initializeEngine(QQmlEngine *engine, const char *uri) = 0;
};
-#define QQmlTypesExtensionInterface_iid "org.qt-project.Qt.QQmlTypesExtensionInterface"
+class Q_QML_EXPORT QQmlEngineExtensionInterface
+{
+public:
+ virtual ~QQmlEngineExtensionInterface() = default;
+ virtual void initializeEngine(QQmlEngine *engine, const char *uri) = 0;
+};
+#define QQmlTypesExtensionInterface_iid "org.qt-project.Qt.QQmlTypesExtensionInterface"
Q_DECLARE_INTERFACE(QQmlTypesExtensionInterface, "org.qt-project.Qt.QQmlTypesExtensionInterface/1.0")
// NOTE: When changing this to a new version and deciding to add backup code to
@@ -73,6 +79,9 @@ Q_DECLARE_INTERFACE(QQmlTypesExtensionInterface, "org.qt-project.Qt.QQmlTypesExt
Q_DECLARE_INTERFACE(QQmlExtensionInterface, QQmlExtensionInterface_iid)
+#define QQmlEngineExtensionInterface_iid "org.qt-project.Qt.QQmlEngineExtensionInterface"
+Q_DECLARE_INTERFACE(QQmlEngineExtensionInterface, QQmlEngineExtensionInterface_iid)
+
QT_END_NAMESPACE
#endif // QQMLEXTENSIONINTERFACE_H
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index a1087a0034..7a62c967e7 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -43,10 +43,11 @@
QT_BEGIN_NAMESPACE
/*!
- \since 5.0
+ \since 5.14
\inmodule QtQml
- \class QQmlExtensionPlugin
- \brief The QQmlExtensionPlugin class provides an abstract base for custom QML extension plugins.
+ \class QQmlEngineExtensionPlugin
+ \brief The QQmlEngineExtensionPlugin class provides an abstract base for custom QML extension
+ plugins.
\ingroup plugins
@@ -60,6 +61,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn void QQmlExtensionPlugin::registerTypes(const char *uri)
+ \internal
Registers the QML types in the given \a uri. Subclasses should implement
this to call qmlRegisterType() for all types which are provided by the extension
@@ -70,26 +72,39 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \internal
+*/
+QQmlExtensionPlugin::QQmlExtensionPlugin(QObject *parent)
+ : QObject(*(new QQmlExtensionPluginPrivate), parent)
+{
+}
+
+/*!
Constructs a QML extension plugin with the given \a parent.
Note that this constructor is invoked automatically by the
Q_PLUGIN_METADATA() macro, so there is no need for calling it
explicitly.
-*/
-QQmlExtensionPlugin::QQmlExtensionPlugin(QObject *parent)
- : QObject(*(new QQmlExtensionPluginPrivate), parent)
+ */
+QQmlEngineExtensionPlugin::QQmlEngineExtensionPlugin(QObject *parent)
+ : QObject(parent)
{
}
+
/*!
\internal
*/
-QQmlExtensionPlugin::~QQmlExtensionPlugin()
-{
-}
+QQmlExtensionPlugin::~QQmlExtensionPlugin() = default;
+
+/*!
+ \internal
+ */
+QQmlEngineExtensionPlugin::~QQmlEngineExtensionPlugin() = default;
/*!
\since 5.1
+ \internal
\brief Returns the URL of the directory from which the extension is loaded.
This is useful when the plugin also needs to load QML files or other
@@ -102,11 +117,21 @@ QUrl QQmlExtensionPlugin::baseUrl() const
}
/*!
+ \internal
+*/
+
+void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
+{
+ Q_UNUSED(engine);
+ Q_UNUSED(uri);
+}
+
+/*!
Initializes the extension from the \a uri using the \a engine. Here an application
plugin might, for example, expose some data or objects to QML,
as context properties on the engine's root context.
-*/
-void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
+ */
+void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
Q_UNUSED(engine);
Q_UNUSED(uri);
@@ -124,6 +149,12 @@ void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
\inmodule QtQml
*/
+/*!
+ \class QQmlEngineExtensionInterface
+ \internal
+ \inmodule QtQml
+*/
+
QT_END_NAMESPACE
#include "moc_qqmlextensionplugin.cpp"
diff --git a/src/qml/qml/qqmlextensionplugin.h b/src/qml/qml/qqmlextensionplugin.h
index 55e9b89dae..ef7ff422cd 100644
--- a/src/qml/qml/qqmlextensionplugin.h
+++ b/src/qml/qml/qqmlextensionplugin.h
@@ -70,6 +70,19 @@ private:
Q_DISABLE_COPY(QQmlExtensionPlugin)
};
+class Q_QML_EXPORT QQmlEngineExtensionPlugin
+ : public QObject
+ , public QQmlEngineExtensionInterface
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QQmlEngineExtensionPlugin)
+ Q_INTERFACES(QQmlEngineExtensionInterface)
+public:
+ explicit QQmlEngineExtensionPlugin(QObject *parent = nullptr);
+ ~QQmlEngineExtensionPlugin() override;
+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
+};
+
QT_END_NAMESPACE
#endif // QQMLEXTENSIONPLUGIN_H
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index a01b07c75c..c3f6a9057d 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -58,6 +58,7 @@
#include <private/qqmltypeloaderqmldircontent_p.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
+#include <QtQml/private/qqmltype_p_p.h>
#include <algorithm>
#include <functional>
@@ -229,8 +230,7 @@ public:
bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor,
QQmlType *type_return, QList<QQmlError> *errors,
QQmlType::RegistrationType registrationType,
- QQmlImport::RecursionRestriction recursionRestriction
- = QQmlImport::PreventRecursion);
+ bool *typeRecursionDetected = nullptr);
QUrl baseUrl;
QString base;
@@ -246,9 +246,9 @@ public:
QQmlTypeLoader *typeLoader;
- static bool locateQmldir(const QString &uri, int vmaj, int vmin,
- QQmlImportDatabase *database,
- QString *outQmldirFilePath, QString *outUrl);
+ static QQmlImports::LocalQmldirResult locateLocalQmldir(
+ const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
+ QString *outQmldirFilePath, QString *outUrl);
static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin,
QList<QQmlError> *errors);
@@ -534,7 +534,7 @@ static QString joinStringRefs(const QVector<QStringRef> &refs, const QChar &sep)
*/
QStringList QQmlImports::completeQmldirPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin)
{
- const QVector<QStringRef> parts = uri.splitRef(Dot, QString::SkipEmptyParts);
+ const QVector<QStringRef> parts = uri.splitRef(Dot, Qt::SkipEmptyParts);
QStringList qmlDirPathsPaths;
// fully & partially versioned parts + 1 unversioned for each base path
@@ -594,7 +594,7 @@ bool QQmlImports::resolveType(const QHashedStringRef &type,
QQmlType *type_return, int *vmaj, int *vmin,
QQmlImportNamespace** ns_return, QList<QQmlError> *errors,
QQmlType::RegistrationType registrationType,
- QQmlImport::RecursionRestriction recursionRestriction) const
+ bool *typeRecursionDetected) const
{
QQmlImportNamespace* ns = d->findQualifiedNamespace(type);
if (ns) {
@@ -604,7 +604,7 @@ bool QQmlImports::resolveType(const QHashedStringRef &type,
}
if (type_return) {
if (d->resolveType(type, vmaj, vmin, type_return, errors, registrationType,
- recursionRestriction)) {
+ typeRecursionDetected)) {
if (qmlImportTrace()) {
#define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
<< ')' << "::resolveType: " << type.toString() << " => "
@@ -614,6 +614,8 @@ bool QQmlImports::resolveType(const QHashedStringRef &type,
RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON";
else if (type_return->isComposite())
RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL";
+ else if (type_return->isInlineComponentType())
+ RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE(INLINECOMPONENT)";
else
RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE";
}
@@ -710,6 +712,38 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
}
const QString typeStr = type.toString();
+ if (isInlineComponent) {
+ Q_ASSERT(type_return);
+ bool ret = uri == typeStr;
+ if (ret) {
+ Q_ASSERT(!type_return->isValid());
+ auto createICType = [&]() {
+ auto typePriv = new QQmlTypePrivate {QQmlType::RegistrationType::InlineComponentType};
+ bool ok = false;
+ typePriv->extraData.id->objectId = QUrl(this->url).fragment().toInt(&ok);
+ Q_ASSERT(ok);
+ typePriv->extraData.id->url = QUrl(this->url);
+ auto icType = QQmlType(typePriv);
+ typePriv->release();
+ return icType;
+ };
+ if (containingType.isValid()) {
+ // we currently cannot reference a Singleton inside itself
+ // in that case, containingType is still invalid
+ if (int icID = containingType.lookupInlineComponentIdByName(typeStr) != -1) {
+ *type_return = containingType.lookupInlineComponentById(icID);
+ } else {
+ auto icType = createICType();
+ int placeholderId = containingType.generatePlaceHolderICId();
+ const_cast<QQmlImportInstance*>(this)->containingType.associateInlineComponent(typeStr, placeholderId, CompositeMetaTypeIds {}, icType);
+ *type_return = QQmlType(icType);
+ }
+ } else {
+ *type_return = createICType();
+ }
+ }
+ return ret;
+ }
QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
if (it != end) {
QString componentUrl;
@@ -744,9 +778,12 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
if (resolveLocalUrl(*base, c.fileName) != componentUrl)
continue; // failed attempt to access an internal type
}
- if (recursionRestriction == QQmlImport::PreventRecursion && *base == componentUrl) {
- if (typeRecursionDetected)
- *typeRecursionDetected = true;
+
+ const bool recursion = *base == componentUrl;
+ if (typeRecursionDetected)
+ *typeRecursionDetected = recursion;
+
+ if (recursionRestriction == QQmlImport::PreventRecursion && recursion) {
continue; // no recursion
}
}
@@ -803,10 +840,10 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
}
if (exists) {
- if (recursionRestriction == QQmlImport::PreventRecursion && base && (*base == qmlUrl)) { // no recursion
- if (typeRecursionDetected)
- *typeRecursionDetected = true;
- } else {
+ const bool recursion = base && *base == qmlUrl;
+ if (typeRecursionDetected)
+ *typeRecursionDetected = recursion;
+ if (recursionRestriction == QQmlImport::AllowRecursion || !recursion) {
QQmlType returnType = QQmlMetaType::typeForUrl(
qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
if (type_return)
@@ -822,49 +859,111 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor,
QQmlType *type_return, QList<QQmlError> *errors,
QQmlType::RegistrationType registrationType,
- QQmlImport::RecursionRestriction recursionRestriction)
+ bool *typeRecursionDetected)
{
- QQmlImportNamespace *s = nullptr;
- int dot = type.indexOf(Dot);
- if (dot >= 0) {
- QHashedStringRef namespaceName(type.constData(), dot);
- s = findQualifiedNamespace(namespaceName);
- if (!s) {
- if (errors) {
- QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString()));
- errors->prepend(error);
- }
- return false;
- }
- int ndot = type.indexOf(Dot,dot+1);
- if (ndot > 0) {
- if (errors) {
- QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
- errors->prepend(error);
- }
- return false;
- }
- } else {
- s = &unqualifiedset;
- }
- QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1);
- if (s) {
- if (s->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors,
- registrationType, recursionRestriction))
+ const QVector<QHashedStringRef> splitName = type.split(Dot);
+ auto resolveTypeInNamespace = [&](QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) -> bool {
+ if (nameSpace->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors,
+ registrationType, typeRecursionDetected))
return true;
- if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) {
+ if (nameSpace->imports.count() == 1 && !nameSpace->imports.at(0)->isLibrary && type_return && nameSpace != &unqualifiedset) {
// qualified, and only 1 url
*type_return = QQmlMetaType::typeForUrl(
- resolveLocalUrl(s->imports.at(0)->url,
+ resolveLocalUrl(nameSpace->imports.at(0)->url,
unqualifiedtype.toString() + QLatin1String(".qml")),
type, false, errors);
return type_return->isValid();
}
+ return false;
+ };
+ switch (splitName.size()) {
+ case 1: {
+ // must be a simple type
+ return resolveTypeInNamespace(type, &unqualifiedset, errors);
}
-
- return false;
+ case 2: {
+ // either namespace + simple type OR simple type + inline component
+ QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
+ if (s) {
+ // namespace + simple type
+ return resolveTypeInNamespace(splitName.at(1), s, errors);
+ } else {
+ if (resolveTypeInNamespace(splitName.at(0), &unqualifiedset, nullptr)) {
+ // either simple type + inline component
+ auto const icName = splitName.at(1).toString();
+ auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
+ if (objectIndex != -1) {
+ *type_return = type_return->lookupInlineComponentById(objectIndex);
+ } else {
+ auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
+ icTypePriv->setContainingType(type_return);
+ icTypePriv->extraData.id->url = type_return->sourceUrl();
+ int placeholderId = type_return->generatePlaceHolderICId();
+ icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
+ auto icType = QQmlType(icTypePriv);
+ icTypePriv->release();
+ type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
+ *type_return = icType;
+ }
+ Q_ASSERT(type_return->containingType().isValid());
+ type_return->setPendingResolutionName(icName);
+ return true;
+ } else {
+ // or a failure
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QQmlImportDatabase::tr("- %1 is neither a type nor a namespace").arg(splitName.at(0).toString()));
+ errors->prepend(error);
+ }
+ return false;
+ }
+ }
+ }
+ case 3: {
+ // must be namespace + simple type + inline component
+ QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
+ QQmlError error;
+ if (!s) {
+ error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString()));
+ } else {
+ if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) {
+ auto const icName = splitName.at(2).toString();
+ auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
+ if (objectIndex != -1)
+ *type_return = type_return->lookupInlineComponentById(objectIndex);
+ else {
+ auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
+ icTypePriv->setContainingType(type_return);
+ icTypePriv->extraData.id->url = type_return->sourceUrl();
+ int placeholderId = type_return->generatePlaceHolderICId();
+ icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
+ auto icType = QQmlType(icTypePriv);
+ icTypePriv->release();
+ type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
+ *type_return = icType;
+ }
+ type_return->setPendingResolutionName(icName);
+ return true;
+ } else {
+ error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString()));
+ }
+ }
+ if (errors) {
+ errors->prepend(error);
+ }
+ return false;
+ }
+ default: {
+ // all other numbers suggest a user error
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
+ errors->prepend(error);
+ }
+ return false;
+ }
+ }
+ Q_UNREACHABLE();
}
QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const
@@ -880,13 +979,25 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS
int *vmajor, int *vminor, QQmlType *type_return,
QString *base, QList<QQmlError> *errors,
QQmlType::RegistrationType registrationType,
- QQmlImport::RecursionRestriction recursionRestriction)
+ bool *typeRecursionDetected)
{
- bool typeRecursionDetected = false;
+ QQmlImport::RecursionRestriction recursionRestriction =
+ typeRecursionDetected ? QQmlImport::AllowRecursion : QQmlImport::PreventRecursion;
+
+ bool localTypeRecursionDetected = false;
+ if (!typeRecursionDetected)
+ typeRecursionDetected = &localTypeRecursionDetected;
+
+ if (needsSorting()) {
+ std::stable_sort(imports.begin(), imports.end(), [](QQmlImportInstance *left, QQmlImportInstance *) {
+ return left->isInlineComponent;
+ });
+ setNeedsSorting(false);
+ }
for (int i=0; i<imports.count(); ++i) {
const QQmlImportInstance *import = imports.at(i);
if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base,
- &typeRecursionDetected, registrationType, recursionRestriction, errors)) {
+ typeRecursionDetected, registrationType, recursionRestriction, errors)) {
if (qmlCheckTypes()) {
// check for type clashes
for (int j = i+1; j<imports.count(); ++j) {
@@ -933,7 +1044,7 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS
}
if (errors) {
QQmlError error;
- if (typeRecursionDetected)
+ if (*typeRecursionDetected)
error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
else
error.setDescription(QQmlImportDatabase::tr("is not a type"));
@@ -942,6 +1053,17 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS
return false;
}
+bool QQmlImportNamespace::needsSorting() const
+{
+ return nextNamespace == this;
+}
+
+void QQmlImportNamespace::setNeedsSorting(bool needsSorting)
+{
+ Q_ASSERT(nextNamespace == this || nextNamespace == nullptr);
+ nextNamespace = needsSorting ? this : nullptr;
+}
+
QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
: ref(1), typeLoader(loader) {
}
@@ -990,7 +1112,9 @@ static QVector<QStaticPlugin> makePlugins()
const auto staticPlugins = QPluginLoader::staticPlugins();
for (const QStaticPlugin &plugin : staticPlugins) {
const QString iid = plugin.metaData().value(QLatin1String("IID")).toString();
- if (iid == QLatin1String(QQmlExtensionInterface_iid) || iid == QLatin1String(QQmlExtensionInterface_iid_old)) {
+ if (iid == QLatin1String(QQmlEngineExtensionInterface_iid)
+ || iid == QLatin1String(QQmlExtensionInterface_iid)
+ || iid == QLatin1String(QQmlExtensionInterface_iid_old)) {
plugins.append(plugin);
}
}
@@ -1008,7 +1132,9 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res
static const QVector<QStaticPlugin> plugins = makePlugins();
for (const QStaticPlugin &plugin : plugins) {
// Since a module can list more than one plugin, we keep iterating even after we found a match.
- if (QQmlExtensionPlugin *instance = qobject_cast<QQmlExtensionPlugin *>(plugin.instance())) {
+ QObject *instance = plugin.instance();
+ if (qobject_cast<QQmlEngineExtensionPlugin *>(instance)
+ || qobject_cast<QQmlExtensionPlugin *>(instance)) {
const QJsonArray metaTagsUriList = plugin.metaData().value(QLatin1String("uri")).toArray();
if (metaTagsUriList.isEmpty()) {
if (errors) {
@@ -1064,7 +1190,13 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
if (qmldirPluginCount == 0)
return true;
- if (!database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
+ if (database->qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
+ if ((vmaj >= 0 && vmin >= 0)
+ ? !QQmlMetaType::isModule(uri, vmaj, vmin)
+ : !QQmlMetaType::isAnyModule(uri)) {
+ QQmlMetaType::qmlRegisterModuleTypes(uri, vmaj);
+ }
+ } else {
// First search for listed qmldir plugins dynamically. If we cannot resolve them all, we continue
// searching static plugins that has correct metadata uri. Note that since we only know the uri
// for a static plugin, and not the filename, we cannot know which static plugin belongs to which
@@ -1225,8 +1357,9 @@ Locates the qmldir file for \a uri version \a vmaj.vmin. Returns true if found,
and fills in outQmldirFilePath and outQmldirUrl appropriately. Otherwise returns
false.
*/
-bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
- QString *outQmldirFilePath, QString *outQmldirPathUrl)
+QQmlImports::LocalQmldirResult QQmlImportsPrivate::locateLocalQmldir(
+ const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database,
+ QString *outQmldirFilePath, QString *outQmldirPathUrl)
{
Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
@@ -1234,20 +1367,21 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ
QQmlImportDatabase::QmldirCache *cacheHead = nullptr;
{
- QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
- if (cachePtr) {
- cacheHead = *cachePtr;
- QQmlImportDatabase::QmldirCache *cache = cacheHead;
- while (cache) {
- if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
- *outQmldirFilePath = cache->qmldirFilePath;
- *outQmldirPathUrl = cache->qmldirPathUrl;
- return !cache->qmldirFilePath.isEmpty();
+ QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
+ if (cachePtr) {
+ cacheHead = *cachePtr;
+ QQmlImportDatabase::QmldirCache *cache = cacheHead;
+ while (cache) {
+ if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
+ *outQmldirFilePath = cache->qmldirFilePath;
+ *outQmldirPathUrl = cache->qmldirPathUrl;
+ return cache->qmldirFilePath.isEmpty() ? QQmlImports::QmldirNotFound
+ : QQmlImports::QmldirFound;
+ }
+ cache = cache->next;
}
- cache = cache->next;
}
}
- }
QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
@@ -1257,12 +1391,17 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ
interceptor ? QQmlImportDatabase::LocalOrRemote : QQmlImportDatabase::Local);
// Search local import paths for a matching version
- const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(uri, localImportPaths, vmaj, vmin);
+ const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
+ uri, localImportPaths, vmaj, vmin);
+ bool pathTurnedRemote = false;
for (QString qmldirPath : qmlDirPaths) {
if (interceptor) {
- qmldirPath = QQmlFile::urlToLocalFileOrQrc(
- interceptor->intercept(QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
- QQmlAbstractUrlInterceptor::QmldirFile));
+ const QUrl intercepted = interceptor->intercept(
+ QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
+ QQmlAbstractUrlInterceptor::QmldirFile);
+ qmldirPath = QQmlFile::urlToLocalFileOrQrc(intercepted);
+ if (!pathTurnedRemote && qmldirPath.isEmpty() && !QQmlFile::isLocalFile(intercepted))
+ pathTurnedRemote = true;
}
QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirPath);
@@ -1285,7 +1424,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ
*outQmldirFilePath = absoluteFilePath;
*outQmldirPathUrl = url;
- return true;
+ return QQmlImports::QmldirFound;
}
}
@@ -1295,7 +1434,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ
cache->next = cacheHead;
database->qmldirCache.insert(uri, cache);
- return false;
+ return pathTurnedRemote ? QQmlImports::QmldirInterceptedToRemote : QQmlImports::QmldirNotFound;
}
bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin,
@@ -1627,6 +1766,22 @@ bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlErro
}
/*!
+ \internal
+ */
+bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType)
+{
+ importInstance->url = importUrl.toString();
+ importInstance->uri = name;
+ importInstance->isInlineComponent = true;
+ importInstance->majversion = 0;
+ importInstance->minversion = 0;
+ importInstance->containingType = containingType;
+ d->unqualifiedset.imports.push_back(importInstance);
+ d->unqualifiedset.setNeedsSorting(true);
+ return true;
+}
+
+/*!
\internal
Adds information to \a imports such that subsequent calls to resolveType()
@@ -1689,11 +1844,11 @@ bool QQmlImports::updateQmldirContent(QQmlImportDatabase *importDb,
return d->updateQmldirContent(uri, prefix, qmldirIdentifier, qmldirUrl, importDb, errors);
}
-bool QQmlImports::locateQmldir(QQmlImportDatabase *importDb,
- const QString& uri, int vmaj, int vmin,
- QString *qmldirFilePath, QString *url)
+QQmlImports::LocalQmldirResult QQmlImports::locateLocalQmldir(
+ QQmlImportDatabase *importDb, const QString &uri, int vmaj, int vmin,
+ QString *qmldirFilePath, QString *url)
{
- return d->locateQmldir(uri, vmaj, vmin, importDb, qmldirFilePath, url);
+ return d->locateLocalQmldir(uri, vmaj, vmin, importDb, qmldirFilePath, url);
}
bool QQmlImports::isLocal(const QString &url)
@@ -1744,7 +1899,7 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
#else
QLatin1Char pathSep(':');
#endif
- QStringList paths = envImportPath.split(pathSep, QString::SkipEmptyParts);
+ QStringList paths = envImportPath.split(pathSep, Qt::SkipEmptyParts);
for (int ii = paths.count() - 1; ii >= 0; --ii)
addImportPath(paths.at(ii));
}
@@ -1756,7 +1911,7 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_BUNDLED_LIBS_PATH"))) {
const QString envImportPath = qEnvironmentVariable("QT_BUNDLED_LIBS_PATH");
QLatin1Char pathSep(':');
- QStringList paths = envImportPath.split(pathSep, QString::SkipEmptyParts);
+ QStringList paths = envImportPath.split(pathSep, Qt::SkipEmptyParts);
for (int ii = paths.count() - 1; ii >= 0; --ii)
addPluginPath(paths.at(ii));
}
@@ -2001,12 +2156,25 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths)
/*!
\internal
*/
-bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &basePath,
- const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
+static bool registerPluginTypes(QObject *instance, const QString &basePath, const QString &uri,
+ const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
{
if (qmlImportTrace())
qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath;
- return QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors);
+
+ if (!QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors))
+ return false;
+
+ if (vmaj >= 0 && !typeNamespace.isEmpty() && !QQmlMetaType::protectModule(uri, vmaj)) {
+ QQmlError error;
+ error.setDescription(
+ QString::fromLatin1("Cannot protect module %1 %2 as it was never registered")
+ .arg(uri).arg(vmaj));
+ errors->append(error);
+ return false;
+ }
+
+ return true;
}
/*!
@@ -2046,17 +2214,8 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba
// other QML loader threads and thus not process the initializeEngine call).
}
- // The plugin's per-engine initialization does not need lock protection, as this function is
- // only called from the engine specific loader thread and importDynamicPlugin as well as
- // importStaticPlugin are the only places of access.
- if (!initializedPlugins.contains(uniquePluginID)) {
- initializedPlugins.insert(uniquePluginID);
-
- if (QQmlExtensionInterface *eiface = qobject_cast<QQmlExtensionInterface *>(instance)) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- ep->typeLoader.initializeEngine(eiface, uri.toUtf8().constData());
- }
- }
+ if (!initializedPlugins.contains(uniquePluginID))
+ finalizePlugin(instance, uniquePluginID, uri);
return true;
}
@@ -2131,22 +2290,46 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr
// other QML loader threads and thus not process the initializeEngine call).
}
+ if (!engineInitialized)
+ finalizePlugin(instance, absoluteFilePath, uri);
- if (!engineInitialized) {
- // The plugin's per-engine initialization does not need lock protection, as this function is
- // only called from the engine specific loader thread and importDynamicPlugin as well as
- // importStaticPlugin are the only places of access.
- initializedPlugins.insert(absoluteFilePath);
+ return true;
+}
- if (QQmlExtensionInterface *eiface = qobject_cast<QQmlExtensionInterface *>(instance)) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- ep->typeLoader.initializeEngine(eiface, uri.toUtf8().constData());
- }
+bool QQmlImportDatabase::removeDynamicPlugin(const QString &filePath)
+{
+ StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
+ QMutexLocker lock(&plugins->mutex);
+
+ auto it = plugins->find(QFileInfo(filePath).absoluteFilePath());
+ if (it == plugins->end())
+ return false;
+
+ QPluginLoader *loader = it->loader;
+ if (!loader)
+ return false;
+
+ if (!loader->unload()) {
+ qWarning("Unloading %s failed: %s", qPrintable(it->uri),
+ qPrintable(loader->errorString()));
}
+ delete loader;
+ plugins->erase(it);
return true;
}
+QStringList QQmlImportDatabase::dynamicPlugins() const
+{
+ StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
+ QMutexLocker lock(&plugins->mutex);
+ QStringList results;
+ for (auto it = plugins->constBegin(), end = plugins->constEnd(); it != end; ++it) {
+ if (it->loader != nullptr)
+ results.append(it.key());
+ }
+ return results;
+}
#endif // QT_CONFIG(library)
void QQmlImportDatabase::clearDirCache()
@@ -2165,4 +2348,20 @@ void QQmlImportDatabase::clearDirCache()
qmldirCache.clear();
}
+void QQmlImportDatabase::finalizePlugin(QObject *instance, const QString &path, const QString &uri)
+{
+ // The plugin's per-engine initialization does not need lock protection, as this function is
+ // only called from the engine specific loader thread and importDynamicPlugin as well as
+ // importStaticPlugin are the only places of access.
+
+ initializedPlugins.insert(path);
+ if (auto *extensionIface = qobject_cast<QQmlExtensionInterface *>(instance)) {
+ QQmlEnginePrivate::get(engine)->typeLoader.initializeEngine(
+ extensionIface, uri.toUtf8().constData());
+ } else if (auto *engineIface = qobject_cast<QQmlEngineExtensionInterface *>(instance)) {
+ QQmlEnginePrivate::get(engine)->typeLoader.initializeEngine(
+ engineIface, uri.toUtf8().constData());
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index ea9c2eafb5..594eae7bb4 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -80,10 +80,12 @@ struct QQmlImportInstance
QString uri; // e.g. QtQuick
QString url; // the base path of the import
QString localDirectoryPath; // the base path of the import if it's a local file
+ QQmlType containingType; // points to the containing type for inline components
int majversion; // the major version imported
int minversion; // the minor version imported
bool isLibrary; // true means that this is not a file import
bool implicitlyImported = false;
+ bool isInlineComponent = false;
QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir
QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir
@@ -114,13 +116,18 @@ public:
int *vmajor, int *vminor, QQmlType* type_return,
QString *base = nullptr, QList<QQmlError> *errors = nullptr,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
- QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion);
+ bool *typeRecursionDeteced = nullptr);
// Prefix when used as a qualified import. Otherwise empty.
QHashedString prefix;
// Used by QQmlImportsPrivate::qualifiedSets
+ // set to this in unqualifiedSet to indicate that the lists of imports needs
+ // to be sorted when an inline component import was added
+ // We can't use flag pointer, as that does not work with QFieldList
QQmlImportNamespace *nextNamespace;
+ bool needsSorting() const;
+ void setNeedsSorting(bool needsSorting);
};
class Q_QML_PRIVATE_EXPORT QQmlImports
@@ -142,8 +149,7 @@ public:
QQmlImportNamespace **ns_return,
QList<QQmlError> *errors = nullptr,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
- QQmlImport::RecursionRestriction recursionRestriction
- = QQmlImport::PreventRecursion) const;
+ bool *typeRecursionDetected = nullptr) const;
bool resolveType(QQmlImportNamespace *,
const QHashedStringRef& type,
QQmlType *type_return, int *version_major, int *version_minor,
@@ -152,6 +158,8 @@ public:
bool addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors);
+ bool addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType);
+
bool addFileImport(QQmlImportDatabase *,
const QString& uri, const QString& prefix, int vmaj, int vmin, bool incomplete,
QList<QQmlError> *errors);
@@ -164,9 +172,15 @@ public:
const QString &uri, const QString &prefix,
const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors);
- bool locateQmldir(QQmlImportDatabase *,
- const QString &uri, int vmaj, int vmin,
- QString *qmldirFilePath, QString *url);
+ enum LocalQmldirResult {
+ QmldirFound,
+ QmldirNotFound,
+ QmldirInterceptedToRemote
+ };
+
+ LocalQmldirResult locateLocalQmldir(
+ QQmlImportDatabase *, const QString &uri, int vmaj, int vmin,
+ QString *qmldirFilePath, QString *url);
void populateCache(QQmlTypeNameCache *cache) const;
@@ -203,7 +217,7 @@ private:
QQmlImportsPrivate *d;
};
-class QQmlImportDatabase
+class Q_QML_PRIVATE_EXPORT QQmlImportDatabase
{
Q_DECLARE_TR_FUNCTIONS(QQmlImportDatabase)
public:
@@ -214,6 +228,8 @@ public:
#if QT_CONFIG(library)
bool importDynamicPlugin(const QString &filePath, const QString &uri, const QString &importNamespace, int vmaj, QList<QQmlError> *errors);
+ bool removeDynamicPlugin(const QString &filePath);
+ QStringList dynamicPlugins() const;
#endif
QStringList importPathList(PathType type = LocalOrRemote) const;
@@ -235,9 +251,8 @@ private:
const QString &baseName);
bool importStaticPlugin(QObject *instance, const QString &basePath, const QString &uri,
const QString &typeNamespace, int vmaj, QList<QQmlError> *errors);
- bool registerPluginTypes(QObject *instance, const QString &basePath,
- const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors);
void clearDirCache();
+ void finalizePlugin(QObject *instance, const QString &path, const QString &uri);
struct QmldirCache {
int versionMajor;
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index bc06226cbf..0ad013e90b 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -43,6 +43,7 @@
#include "qqmlexpression_p.h"
#include "qqmlobjectcreator_p.h"
+#include <private/qqmlcomponent_p.h>
void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext)
{
@@ -60,7 +61,7 @@ void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext)
QExplicitlySharedDataPointer<QQmlIncubatorPrivate> parentIncubator;
QQmlContextData *cctxt = forContext;
while (cctxt) {
- if (cctxt->incubator) {
+ if (!cctxt->hasExtraObject && cctxt->incubator) {
parentIncubator = cctxt->incubator;
break;
}
@@ -148,7 +149,8 @@ void QQmlIncubatorPrivate::clear()
}
enginePriv = nullptr;
if (!rootContext.isNull()) {
- rootContext->incubator = nullptr;
+ if (!rootContext->hasExtraObject)
+ rootContext->incubator = nullptr;
rootContext = nullptr;
}
@@ -296,6 +298,20 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
tresult = creator->create(subComponentToCreate, /*parent*/nullptr, &i);
if (!tresult)
errors = creator->errors;
+ else {
+ RequiredProperties& requiredProperties = creator->requiredProperties();
+ for (auto it = initialProperties.cbegin(); it != initialProperties.cend(); ++it) {
+ auto component = tresult;
+ auto name = it.key();
+ QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(component, name, requiredProperties);
+ if (!prop.isValid() || !prop.write(it.value())) {
+ QQmlError error{};
+ error.setUrl(compilationUnit->url());
+ error.setDescription(QLatin1String("Could not set property %1").arg(name));
+ errors.push_back(error);
+ }
+ }
+ }
enginePriv->dereferenceScarceResources();
if (watcher.hasRecursed())
@@ -312,8 +328,14 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
ddata->indestructible = true;
ddata->explicitIndestructibleSet = true;
ddata->rootObjectInCreation = false;
- if (q)
+ if (q) {
q->setInitialState(result);
+ if (!creator->requiredProperties().empty()) {
+ const auto& unsetRequiredProperties = creator->requiredProperties();
+ for (const auto& unsetRequiredProperty: unsetRequiredProperties)
+ errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
+ }
+ }
}
if (watcher.hasRecursed())
@@ -381,26 +403,51 @@ void QQmlIncubationController::incubateFor(int msecs)
if (!d || !d->incubatorCount)
return;
- QQmlInstantiationInterrupt i(msecs * 1000000);
+ QQmlInstantiationInterrupt i(msecs * Q_INT64_C(1000000));
i.reset();
do {
static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
} while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
}
+#if QT_DEPRECATED_SINCE(5, 15)
/*!
-Incubate objects while the bool pointed to by \a flag is true, or until there are no
-more objects to incubate, or up to \a msecs if \a msecs is not zero.
+\obsolete
+
+\warning Do not use this function.
+Use the overload taking a \c{std::atomic<bool>} instead.
+*/
+void QQmlIncubationController::incubateWhile(volatile bool *flag, int msecs)
+{
+ if (!d || !d->incubatorCount)
+ return;
+
+ QQmlInstantiationInterrupt i(flag, msecs * Q_INT64_C(1000000));
+ i.reset();
+ do {
+ static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
+ } while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
+}
+#endif
+
+/*!
+\since 5.15
+
+Incubate objects while the atomic bool pointed to by \a flag is true,
+or until there are no more objects to incubate, or up to \a msecs if \a
+msecs is not zero.
Generally this method is used in conjunction with a thread or a UNIX signal that sets
the bool pointed to by \a flag to false when it wants incubation to be interrupted.
+
+\note \a flag is read using acquire memory ordering.
*/
-void QQmlIncubationController::incubateWhile(volatile bool *flag, int msecs)
+void QQmlIncubationController::incubateWhile(std::atomic<bool> *flag, int msecs)
{
if (!d || !d->incubatorCount)
return;
- QQmlInstantiationInterrupt i(flag, msecs * 1000000);
+ QQmlInstantiationInterrupt i(flag, msecs * Q_INT64_C(1000000));
i.reset();
do {
static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
@@ -657,6 +704,36 @@ QObject *QQmlIncubator::object() const
}
/*!
+Return a list of properties which are required but haven't been set yet.
+This list can be modified, so that subclasses which implement special logic
+setInitialProperties can mark properties set there as no longer required.
+
+\sa QQmlIncubator::setInitialProperties
+\since 5.15
+*/
+RequiredProperties &QQmlIncubatorPrivate::requiredProperties()
+{
+ return creator->requiredProperties();
+}
+
+bool QQmlIncubatorPrivate::hadRequiredProperties() const
+{
+ return creator->componentHadRequiredProperties();
+}
+
+/*!
+Stores a mapping from property names to initial values, contained in
+\a initialProperties, with which the incubated component will be initialized.
+
+\sa QQmlComponent::setInitialProperties
+\since 5.15
+*/
+void QQmlIncubator::setInitialProperties(const QVariantMap &initialProperties)
+{
+ d->initialProperties = initialProperties;
+}
+
+/*!
Called when the status of the incubator changes. \a status is the new status.
The default implementation does nothing.
diff --git a/src/qml/qml/qqmlincubator.h b/src/qml/qml/qqmlincubator.h
index e68f6e3c45..6e47ca2173 100644
--- a/src/qml/qml/qqmlincubator.h
+++ b/src/qml/qml/qqmlincubator.h
@@ -42,11 +42,15 @@
#include <QtQml/qtqmlglobal.h>
#include <QtQml/qqmlerror.h>
+#include <atomic>
QT_BEGIN_NAMESPACE
class QQmlEngine;
+class QQmlPropertyData;
+class QVariant;
+using QVariantMap = QMap<QString, QVariant>;
class QQmlIncubatorPrivate;
class Q_QML_EXPORT QQmlIncubator
@@ -84,6 +88,8 @@ public:
QObject *object() const;
+ void setInitialProperties(const QVariantMap &initialProperties);
+
protected:
virtual void statusChanged(Status);
virtual void setInitialState(QObject *);
@@ -107,7 +113,11 @@ public:
int incubatingObjectCount() const;
void incubateFor(int msecs);
+#if QT_DEPRECATED_SINCE(5, 15)
+ QT_DEPRECATED_VERSION_X(5, 15, "Use the overload that takes a std::atomic<bool>")
void incubateWhile(volatile bool *flag, int msecs=0);
+#endif
+ void incubateWhile(std::atomic<bool> *flag, int msecs = 0);
protected:
virtual void incubatingObjectCountChanged(int);
diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h
index 57ec8249cb..a674ff274f 100644
--- a/src/qml/qml/qqmlincubator_p.h
+++ b/src/qml/qml/qqmlincubator_p.h
@@ -61,8 +61,10 @@
QT_BEGIN_NAMESPACE
+class RequiredProperties;
+
class QQmlIncubator;
-class QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator
+class Q_QML_PRIVATE_EXPORT QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator
{
public:
QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m);
@@ -97,11 +99,14 @@ public:
QIntrusiveList<QIPBase, &QIPBase::nextWaitingFor> waitingFor;
QRecursionNode recursion;
+ QVariantMap initialProperties;
void clear();
void forceCompletion(QQmlInstantiationInterrupt &i);
void incubate(QQmlInstantiationInterrupt &i);
+ RequiredProperties &requiredProperties();
+ bool hadRequiredProperties() const;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlinfo.cpp b/src/qml/qml/qqmlinfo.cpp
index dd401bdb20..7204d5ccd2 100644
--- a/src/qml/qml/qqmlinfo.cpp
+++ b/src/qml/qml/qqmlinfo.cpp
@@ -44,6 +44,7 @@
#include "qqmlcontext_p.h"
#include "qqmlmetatype_p.h"
#include "qqmlengine_p.h"
+#include "qqmlsourcecoordinate_p.h"
#include <QCoreApplication>
@@ -218,8 +219,8 @@ QQmlInfo::~QQmlInfo()
QQmlData *ddata = QQmlData::get(object, false);
if (ddata && ddata->outerContext) {
error.setUrl(ddata->outerContext->url());
- error.setLine(ddata->lineNumber);
- error.setColumn(ddata->columnNumber);
+ error.setLine(qmlConvertSourceCoordinate<quint16, int>(ddata->lineNumber));
+ error.setColumn(qmlConvertSourceCoordinate<quint16, int>(ddata->columnNumber));
}
}
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
index 82cad8eba8..f66094fad7 100644
--- a/src/qml/qml/qqmlirloader.cpp
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -78,14 +78,14 @@ struct FakeExpression : public QQmlJS::AST::NullExpression
: location(start, length)
{}
- virtual QQmlJS::AST::SourceLocation firstSourceLocation() const
+ virtual QQmlJS::SourceLocation firstSourceLocation() const
{ return location; }
- virtual QQmlJS::AST::SourceLocation lastSourceLocation() const
+ virtual QQmlJS::SourceLocation lastSourceLocation() const
{ return location; }
private:
- QQmlJS::AST::SourceLocation location;
+ QQmlJS::SourceLocation location;
};
QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject)
@@ -200,6 +200,13 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
object->runtimeFunctionIndices.allocate(pool, functionIndices);
+ const QV4::CompiledData::InlineComponent *serializedInlineComponent = serializedObject->inlineComponentTable();
+ for (uint i = 0; i < serializedObject->nInlineComponents; ++i, ++serializedInlineComponent) {
+ QmlIR::InlineComponent *ic = pool->New<QmlIR::InlineComponent>();
+ *static_cast<QV4::CompiledData::InlineComponent*>(ic) = *serializedInlineComponent;
+ object->inlineComponents->append(ic);
+ }
+
return object;
}
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 8661ebcc13..6a9ef06159 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -50,6 +50,7 @@
#include <private/qqmlglobal_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
QT_BEGIN_NAMESPACE
@@ -72,8 +73,8 @@ bool QQmlDelayedError::addError(QQmlEnginePrivate *e)
void QQmlDelayedError::setErrorLocation(const QQmlSourceLocation &sourceLocation)
{
m_error.setUrl(QUrl(sourceLocation.sourceFile));
- m_error.setLine(sourceLocation.line);
- m_error.setColumn(sourceLocation.column);
+ m_error.setLine(qmlConvertSourceCoordinate<quint16, int>(sourceLocation.line));
+ m_error.setColumn(qmlConvertSourceCoordinate<quint16, int>(sourceLocation.column));
}
void QQmlDelayedError::setErrorDescription(const QString &description)
diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp
index 5425bf498c..b504fdf22d 100644
--- a/src/qml/qml/qqmllist.cpp
+++ b/src/qml/qml/qqmllist.cpp
@@ -250,10 +250,37 @@ bool QQmlListReference::canCount() const
}
/*!
- Return true if at(), count(), append() and clear() are implemented, so you can manipulate
- the list.
+Returns true if items in the list property can be replaced, otherwise false.
+Returns false if the reference is invalid.
+
+\sa replace()
+*/
+bool QQmlListReference::canReplace() const
+{
+ return (isValid() && d->property.replace);
+}
+
+/*!
+Returns true if the last item can be removed from the list property, otherwise false.
+Returns false if the reference is invalid.
+
+\sa removeLast()
+*/
+bool QQmlListReference::canRemoveLast() const
+{
+ return (isValid() && d->property.removeLast);
+}
+
+/*!
+ Return true if at(), count(), append(), and either clear() or removeLast()
+ are implemented, so you can manipulate the list.
+
+ Mind that replace() and removeLast() can be emulated by stashing all
+ items and rebuilding the list using clear() and append(). Therefore,
+ they are not required for the list to be manipulable. Furthermore,
+ clear() can be emulated using removeLast().
-\sa isReadable(), at(), count(), append(), clear()
+\sa isReadable(), at(), count(), append(), clear(), replace(), removeLast()
*/
bool QQmlListReference::isManipulable() const
{
@@ -329,6 +356,39 @@ int QQmlListReference::count() const
}
/*!
+Replaces the item at \a index in the list with \a object.
+Returns true if the operation succeeded, otherwise false.
+
+\sa canReplace()
+*/
+bool QQmlListReference::replace(int index, QObject *object) const
+{
+ if (!canReplace())
+ return false;
+
+ if (object && !QQmlMetaObject::canConvert(object, d->elementType))
+ return false;
+
+ d->property.replace(&d->property, index, object);
+ return true;
+}
+
+/*!
+Removes the last item in the list.
+Returns true if the operation succeeded, otherwise false.
+
+\sa canRemoveLast()
+*/
+bool QQmlListReference::removeLast() const
+{
+ if (!canRemoveLast())
+ return false;
+
+ d->property.removeLast(&d->property);
+ return true;
+}
+
+/*!
\class QQmlListProperty
\since 5.0
\inmodule QtQml
@@ -375,14 +435,25 @@ QML list properties are type-safe - in this case \c {Fruit} is a QObject type th
/*!
\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> &list)
+\deprecated
Convenience constructor for making a QQmlListProperty value from an existing
QList \a list. The \a list reference must remain valid for as long as \a object
exists. \a object must be provided.
-Generally this constructor should not be used in production code, as a
-writable QList violates QML's memory management rules. However, this constructor
-can be very useful while prototyping.
+This constructor synthesizes the removeLast() and replace() methods
+introduced in Qt 5.15, using count(), at(), clear(), and append(). This is slow.
+If you intend to manipulate the list beyond clearing it, you should explicitly
+provide these methods.
+*/
+
+/*!
+\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> *list)
+\since 5.15
+
+Convenience constructor for making a QQmlListProperty value from an existing
+QList \a list. The \a list reference must remain valid for as long as \a object
+exists. \a object must be provided.
*/
/*!
@@ -408,6 +479,39 @@ remains valid while \a object exists.
Null pointers can be passed for any function. If any null pointers are passed in, the list
will be neither designable nor alterable by the debugger. It is recommended to provide valid
pointers for all functions.
+
+\note The resulting QQmlListProperty will synthesize the removeLast() and
+replace() methods using \a count, \a at, \a clear, and \a append if all of those
+are given. This is slow. If you intend to manipulate the list beyond clearing it,
+you should explicitly provide these methods.
+*/
+
+/*!
+\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(
+ QObject *object, void *data, AppendFunction append, CountFunction count,
+ AtFunction at, ClearFunction clear, ReplaceFunction replace,
+ RemoveLastFunction removeLast)
+
+Construct a QQmlListProperty from a set of operation functions \a append,
+\a count, \a at, \a clear, \a replace, and \removeLast. An opaque \a data handle
+may be passed which can be accessed from within the operation functions. The
+list property remains valid while \a object exists.
+
+Null pointers can be passed for any function, causing the respective function to
+be synthesized using the others, if possible. QQmlListProperty can synthesize
+\list
+ \li \a clear using \a count and \a removeLast
+ \li \a replace using \a count, \a at, \a clear, and \a append
+ \li \a replace using \a count, \a at, \a removeLast, and \a append
+ \li \a removeLast using \a count, \a at, \a clear, and \a append
+\endlist
+if those are given. This is slow, but if your list does not natively provide
+faster options for these primitives, you may want to use the synthesized ones.
+
+Furthermore, if either of \a count, \a at, \a append, and \a clear are neither
+given explicitly nor synthesized, the list will be neither designable nor
+alterable by the debugger. It is recommended to provide enough valid pointers
+to avoid this situation.
*/
/*!
@@ -448,4 +552,20 @@ Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
Clear the list \a property.
*/
+/*!
+\typedef QQmlListProperty::ReplaceFunction
+
+Synonym for \c {void (*)(QQmlListProperty<T> *property, int index, T *value)}.
+
+Replace the element at position \a index in the list \a property with \a value.
+*/
+
+/*!
+\typedef QQmlListProperty::RemoveLastFunction
+
+Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
+
+Remove the last element from the list \a property.
+*/
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmllist.h b/src/qml/qml/qqmllist.h
index 90ec57c911..17333ca9e4 100644
--- a/src/qml/qml/qqmllist.h
+++ b/src/qml/qml/qqmllist.h
@@ -55,22 +55,28 @@ struct QMetaObject;
template<typename T>
class QQmlListProperty {
public:
- typedef void (*AppendFunction)(QQmlListProperty<T> *, T*);
- typedef int (*CountFunction)(QQmlListProperty<T> *);
- typedef T *(*AtFunction)(QQmlListProperty<T> *, int);
- typedef void (*ClearFunction)(QQmlListProperty<T> *);
-
- QQmlListProperty()
- : append(nullptr),
- count(nullptr),
- at(nullptr),
- clear(nullptr)
- {}
+ using AppendFunction = void (*)(QQmlListProperty<T> *, T *);
+ using CountFunction = int (*)(QQmlListProperty<T> *);
+ using AtFunction = T *(*)(QQmlListProperty<T> *, int);
+ using ClearFunction = void (*)(QQmlListProperty<T> *);
+ using ReplaceFunction = void (*)(QQmlListProperty<T> *, int, T *);
+ using RemoveLastFunction = void (*)(QQmlListProperty<T> *);
+
+ QQmlListProperty() = default;
+
+#if QT_DEPRECATED_SINCE(5,15)
+ QT_DEPRECATED_X("Use constructor taking QList pointer, and gain improved performance")
QQmlListProperty(QObject *o, QList<T *> &list)
: object(o), data(&list), append(qlist_append), count(qlist_count), at(qlist_at),
- clear(qlist_clear)
+ clear(qlist_clear), replace(qslow_replace), removeLast(qslow_removeLast)
+ {}
+#endif
+ QQmlListProperty(QObject *o, QList<T *> *list)
+ : object(o), data(list), append(qlist_append), count(qlist_count), at(qlist_at),
+ clear(qlist_clear), replace(qlist_replace), removeLast(qlist_removeLast)
{}
+
QQmlListProperty(QObject *o, void *d, AppendFunction a, CountFunction c, AtFunction t,
ClearFunction r )
: object(o),
@@ -78,37 +84,47 @@ public:
append(a),
count(c),
at(t),
- clear(r)
-
+ clear(r),
+ replace((a && c && t && r) ? qslow_replace : nullptr),
+ removeLast((a && c && t && r) ? qslow_removeLast : nullptr)
{}
- QQmlListProperty(QObject *o, void *d, CountFunction c, AtFunction t)
+
+ QQmlListProperty(QObject *o, void *d, AppendFunction a, CountFunction c, AtFunction t,
+ ClearFunction r, ReplaceFunction s, RemoveLastFunction p)
: object(o),
data(d),
- append(nullptr),
- count(c), at(t),
- clear(nullptr)
+ append(a),
+ count(c),
+ at(t),
+ clear((!r && p && c) ? qslow_clear : r),
+ replace((!s && a && c && t && (r || p)) ? qslow_replace : s),
+ removeLast((!p && a && c && t && r) ? qslow_removeLast : p)
+ {}
+
+ QQmlListProperty(QObject *o, void *d, CountFunction c, AtFunction a)
+ : object(o), data(d), count(c), at(a)
{}
+
bool operator==(const QQmlListProperty &o) const {
return object == o.object &&
data == o.data &&
append == o.append &&
count == o.count &&
at == o.at &&
- clear == o.clear;
+ clear == o.clear &&
+ replace == o.replace &&
+ removeLast == o.removeLast;
}
QObject *object = nullptr;
void *data = nullptr;
- AppendFunction append;
-
- CountFunction count;
- AtFunction at;
-
- ClearFunction clear;
-
- void *dummy1 = nullptr;
- void *dummy2 = nullptr;
+ AppendFunction append = nullptr;
+ CountFunction count = nullptr;
+ AtFunction at = nullptr;
+ ClearFunction clear = nullptr;
+ ReplaceFunction replace = nullptr;
+ RemoveLastFunction removeLast = nullptr;
private:
static void qlist_append(QQmlListProperty *p, T *v) {
@@ -123,6 +139,59 @@ private:
static void qlist_clear(QQmlListProperty *p) {
return reinterpret_cast<QList<T *> *>(p->data)->clear();
}
+ static void qlist_replace(QQmlListProperty *p, int idx, T *v) {
+ return reinterpret_cast<QList<T *> *>(p->data)->replace(idx, v);
+ }
+ static void qlist_removeLast(QQmlListProperty *p) {
+ return reinterpret_cast<QList<T *> *>(p->data)->removeLast();
+ }
+
+ static void qslow_replace(QQmlListProperty<T> *list, int idx, T *v)
+ {
+ const int length = list->count(list);
+ if (idx < 0 || idx >= length)
+ return;
+
+ QVector<T *> stash;
+ if (list->clear != qslow_clear) {
+ stash.reserve(length);
+ for (int i = 0; i < length; ++i)
+ stash.append(i == idx ? v : list->at(list, i));
+ list->clear(list);
+ for (T *item : qAsConst(stash))
+ list->append(list, item);
+ } else {
+ stash.reserve(length - idx - 1);
+ for (int i = length - 1; i > idx; --i) {
+ stash.append(list->at(list, i));
+ list->removeLast(list);
+ }
+ list->removeLast(list);
+ list->append(list, v);
+ while (!stash.isEmpty())
+ list->append(list, stash.takeLast());
+ }
+ }
+
+ static void qslow_clear(QQmlListProperty<T> *list)
+ {
+ for (int i = 0, end = list->count(list); i < end; ++i)
+ list->removeLast(list);
+ }
+
+ static void qslow_removeLast(QQmlListProperty<T> *list)
+ {
+ const int length = list->count(list) - 1;
+ if (length < 0)
+ return;
+ QVector<T *> stash;
+ stash.reserve(length);
+ for (int i = 0; i < length; ++i)
+ stash.append(list->at(list, i));
+ list->clear(list);
+ for (T *item : qAsConst(stash))
+ list->append(list, item);
+ }
};
#endif
@@ -146,6 +215,8 @@ public:
bool canAt() const;
bool canClear() const;
bool canCount() const;
+ bool canReplace() const;
+ bool canRemoveLast() const;
bool isManipulable() const;
bool isReadable() const;
@@ -154,6 +225,8 @@ public:
QObject *at(int) const;
bool clear() const;
int count() const;
+ bool replace(int, QObject *) const;
+ bool removeLast() const;
private:
friend class QQmlListReferencePrivate;
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 5349572921..e5c1cb04a9 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -119,11 +119,13 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
if (hasProperty)
*hasProperty = false;
return Value::undefinedValue().asReturnedValue();
- } else if (id.isString()) {
- if (id == v4->id_length()->propertyKey() && !w->d()->object.isNull()) {
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- return Value::fromUInt32(count).asReturnedValue();
- }
+ }
+
+ if (id.isString() && id == v4->id_length()->propertyKey()) {
+ if (hasProperty)
+ *hasProperty = true;
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ return Value::fromUInt32(count).asReturnedValue();
}
return Object::virtualGet(m, id, receiver, hasProperty);
@@ -131,12 +133,70 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- // doesn't do anything. Should we throw?
- Q_UNUSED(m);
- Q_UNUSED(id);
- Q_UNUSED(value);
- Q_UNUSED(receiver);
- return false;
+ Q_ASSERT(m->as<QmlListWrapper>());
+
+ const auto *w = static_cast<const QmlListWrapper *>(m);
+ QV4::ExecutionEngine *v4 = w->engine();
+
+ QQmlListProperty<QObject> *prop = &(w->d()->property());
+
+ if (id.isArrayIndex()) {
+ if (!prop->count || !prop->replace)
+ return false;
+
+ const uint index = id.asArrayIndex();
+ const int count = prop->count(prop);
+ if (count < 0 || index >= uint(count))
+ return false;
+
+ QV4::Scope scope(v4);
+ QV4::ScopedObject so(scope, value.toObject(scope.engine));
+ if (auto *wrapper = so->as<QV4::QObjectWrapper>()) {
+ prop->replace(prop, index, wrapper->object());
+ return true;
+ }
+
+ return false;
+ }
+
+ if (id.isString() && id == v4->id_length()->propertyKey()) {
+ if (!prop->count)
+ return false;
+
+ const quint32 count = prop->count(prop);
+
+ bool ok = false;
+ const uint newLength = value.asArrayLength(&ok);
+ if (!ok)
+ return false;
+
+ if (newLength == 0) {
+ if (!prop->clear)
+ return false;
+ prop->clear(prop);
+ return true;
+ }
+
+ if (newLength < count) {
+ if (!prop->removeLast)
+ return false;
+
+ for (uint i = newLength; i < count; ++i)
+ prop->removeLast(prop);
+
+ return true;
+ }
+
+ if (!prop->append)
+ return false;
+
+ for (uint i = count; i < newLength; ++i)
+ prop->append(prop, nullptr);
+
+ return true;
+ }
+
+ return Object::virtualPut(m, id, value, receiver);
}
struct QmlListWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index dca13ac8d4..7af6af276a 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -314,7 +314,7 @@ ReturnedValue QQmlDateExtension::method_fromLocaleDateString(const QV4::Function
QLocale locale;
QString dateString = s->toQString();
QDate date = locale.toDate(dateString);
- RETURN_RESULT(engine->newDateObject(QDateTime(date)));
+ RETURN_RESULT(engine->newDateObject(date.startOfDay()));
}
}
@@ -341,7 +341,7 @@ ReturnedValue QQmlDateExtension::method_fromLocaleDateString(const QV4::Function
dt = r->d()->locale->toDate(dateString, enumFormat);
}
- RETURN_RESULT(engine->newDateObject(QDateTime(dt)));
+ RETURN_RESULT(engine->newDateObject(dt.startOfDay()));
}
ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int argc)
@@ -478,6 +478,23 @@ ReturnedValue QQmlLocaleData::method_get_firstDayOfWeek(const QV4::FunctionObjec
RETURN_RESULT(fdow);
}
+ReturnedValue QQmlLocaleData::method_get_numberOptions(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) {
+ QV4::Scope scope(b);
+ const QLocale *locale = getThisLocale(scope, thisObject);
+ if (!locale)
+ return Encode::undefined();
+ int numberOptions = int(locale->numberOptions());
+ RETURN_RESULT(numberOptions);
+}
+
+ReturnedValue QQmlLocaleData::method_set_numberOptions(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) {
+ QV4::Scope scope(b);
+ QLocale *locale = getThisLocale(scope, thisObject);
+ int const numberOptions = argc ? int(argv[0].toNumber()) : QLocale::DefaultNumberOptions;
+ locale->setNumberOptions(QLocale::NumberOptions {numberOptions});
+ return Encode::undefined();
+}
+
ReturnedValue QQmlLocaleData::method_get_measurementSystem(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -701,6 +718,7 @@ QV4LocaleDataDeletable::QV4LocaleDataDeletable(QV4::ExecutionEngine *engine)
o->defineAccessorProperty(QStringLiteral("amText"), QQmlLocaleData::method_get_amText, nullptr);
o->defineAccessorProperty(QStringLiteral("measurementSystem"), QQmlLocaleData::method_get_measurementSystem, nullptr);
o->defineAccessorProperty(QStringLiteral("exponential"), QQmlLocaleData::method_get_exponential, nullptr);
+ o->defineAccessorProperty(QStringLiteral("numberOptions"), QQmlLocaleData::method_get_numberOptions, QQmlLocaleData::method_set_numberOptions);
prototype.set(engine, o);
}
@@ -873,6 +891,16 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
*/
/*!
+ \qmlproperty enumeration QtQml::Locale::NumberOption
+
+ Holds a set of options for number-to-string and
+ string-to-number conversions.
+
+ \sa Number::toLocaleString()
+ \sa Number::fromLocaleString()
+*/
+
+/*!
\qmlproperty string QtQml::Locale::percent
Holds the percent character of this locale.
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 859c36e11b..1d6fdb12a7 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -94,6 +94,9 @@ private:
class Q_QML_PRIVATE_EXPORT QQmlLocale
{
Q_GADGET
+ QML_NAMED_ELEMENT(Locale)
+ QML_UNCREATABLE("Locale cannot be instantiated. Use Qt.locale().")
+ QML_ADDED_IN_MINOR_VERSION(2)
public:
~QQmlLocale();
@@ -128,6 +131,16 @@ public:
Saturday = Qt::Saturday
};
Q_ENUM(DayOfWeek)
+ enum NumberOptions {
+ DefaultNumberOptions = QLocale::DefaultNumberOptions,
+ OmitGroupSeparator = QLocale::OmitGroupSeparator,
+ RejectGroupSeparator = QLocale::RejectGroupSeparator,
+ OmitLeadingZeroInExponent = QLocale::OmitLeadingZeroInExponent,
+ RejectLeadingZeroInExponent = QLocale::RejectLeadingZeroInExponent,
+ IncludeTrailingZeroesAfterDot = QLocale::IncludeTrailingZeroesAfterDot,
+ RejectTrailingZeroesAfterDot = QLocale::RejectTrailingZeroesAfterDot
+ };
+ Q_ENUM(NumberOptions)
static QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName);
static QV4::ReturnedValue wrap(QV4::ExecutionEngine *engine, const QLocale &locale);
@@ -197,6 +210,9 @@ struct QQmlLocaleData : public QV4::Object
static QV4::ReturnedValue method_get_exponential(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_amText(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_pmText(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+
+ static QV4::ReturnedValue method_get_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_set_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
}
diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h
index ee5d9af2e7..c7377528b4 100644
--- a/src/qml/qml/qqmlloggingcategory_p.h
+++ b/src/qml/qml/qqmlloggingcategory_p.h
@@ -56,6 +56,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qqml.h>
QT_BEGIN_NAMESPACE
@@ -66,6 +67,8 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 12)
+ QML_NAMED_ELEMENT(LoggingCategory)
+ QML_ADDED_IN_MINOR_VERSION(8)
public:
enum DefaultLogLevel {
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 4f2089b60d..b2a769cab4 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -92,8 +92,13 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data,
d->typeId = type.typeId;
d->listId = type.listId;
d->isSetup = true;
- d->version_maj = 0;
d->version_min = 0;
+ if (type.version > 0) {
+ d->module = QString::fromUtf8(type.uri);
+ d->version_maj = type.versionMajor;
+ } else {
+ d->version_maj = 0;
+ }
data->registerType(d);
return d;
}
@@ -154,7 +159,7 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el
d->extraData.cd->propertyValueSourceCast = type.valueSourceCast;
d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast;
d->extraData.cd->extFunc = type.extensionObjectCreate;
- d->extraData.cd->customParser = type.customParser;
+ d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
d->extraData.cd->registerEnumClassesUnscoped = true;
if (type.extensionMetaObject)
@@ -267,6 +272,48 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
+void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, int majorVersion,
+ void (*registerFunction)())
+{
+ const QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion);
+ QQmlMetaTypeDataPtr data;
+ if (data->moduleTypeRegistrationFunctions.contains(versionedUri))
+ qFatal("Cannot add multiple registrations for %s %d", qPrintable(uri), majorVersion);
+ else
+ data->moduleTypeRegistrationFunctions.insert(versionedUri, registerFunction);
+}
+
+void QQmlMetaType::qmlRemoveModuleRegistration(const QString &uri, int majorVersion)
+{
+ const QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion);
+ QQmlMetaTypeDataPtr data;
+
+ if (!data.isValid())
+ return; // shutdown/deletion race. Not a problem.
+
+ if (!data->moduleTypeRegistrationFunctions.contains(versionedUri))
+ qFatal("Cannot remove multiple registrations for %s %d", qPrintable(uri), majorVersion);
+ else
+ data->moduleTypeRegistrationFunctions.remove(versionedUri);
+}
+
+bool QQmlMetaType::qmlRegisterModuleTypes(const QString &uri, int majorVersion)
+{
+ QQmlMetaTypeDataPtr data;
+ return data->registerModuleTypes(QQmlMetaTypeData::VersionedUri(uri, majorVersion));
+}
+
+/*!
+ \internal
+ Method is only used to in tst_qqmlenginecleanup.cpp to test whether all
+ types have been removed from qmlLists after shutdown of QQmlEngine
+ */
+int QQmlMetaType::qmlRegisteredListTypeCount()
+{
+ QQmlMetaTypeDataPtr data;
+ return data->qmlLists.count();
+}
+
void QQmlMetaType::clearTypeRegistrations()
{
//Only cleans global static, assumed no running engine
@@ -303,7 +350,7 @@ void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFun
QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type)
{
- if (type.version > 0)
+ if (type.version > 1)
qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
QQmlMetaTypeDataPtr data;
@@ -312,9 +359,6 @@ QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &t
data->idToType.insert(priv->typeId, priv);
data->idToType.insert(priv->listId, priv);
- // XXX No insertMulti, so no multi-version interfaces?
- if (!priv->elementName.isEmpty())
- data->nameToType.insert(priv->elementName, priv);
if (data->interfaces.size() <= type.typeId)
data->interfaces.resize(type.typeId + 16);
@@ -342,7 +386,7 @@ QString registrationTypeString(QQmlType::RegistrationType typeType)
// NOTE: caller must hold a QMutexLocker on "data"
bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data,
- const char *uri, const QString &typeName, int majorVersion = -1)
+ const char *uri, const QString &typeName, int majorVersion)
{
if (!typeName.isEmpty()) {
if (typeName.at(0).isLower()) {
@@ -363,27 +407,16 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
if (uri && !typeName.isEmpty()) {
QString nameSpace = QString::fromUtf8(uri);
-
- if (data->typeRegistrationNamespace.isEmpty() && !nameSpace.isEmpty()) {
- // Is the target namespace protected against further registrations?
- if (data->protectedNamespaces.contains(nameSpace)) {
+ QQmlMetaTypeData::VersionedUri versionedUri;
+ versionedUri.uri = nameSpace;
+ versionedUri.majorVersion = majorVersion;
+ if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){
+ if (qqtm->isLocked()){
QString failure(QCoreApplication::translate("qmlRegisterType",
- "Cannot install %1 '%2' into protected namespace '%3'"));
- data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace));
+ "Cannot install %1 '%2' into protected module '%3' version '%4'"));
+ data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion));
return false;
}
- } else if (majorVersion >= 0) {
- QQmlMetaTypeData::VersionedUri versionedUri;
- versionedUri.uri = nameSpace;
- versionedUri.majorVersion = majorVersion;
- if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){
- if (qqtm->isLocked()){
- QString failure(QCoreApplication::translate("qmlRegisterType",
- "Cannot install %1 '%2' into protected module '%3' version '%4'"));
- data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion));
- return false;
- }
- }
}
}
@@ -408,10 +441,10 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data)
Q_ASSERT(type);
if (!type->elementName.isEmpty())
- data->nameToType.insertMulti(type->elementName, type);
+ data->nameToType.insert(type->elementName, type);
if (type->baseMetaObject)
- data->metaObjectToType.insertMulti(type->baseMetaObject, type);
+ data->metaObjectToType.insert(type->baseMetaObject, type);
if (type->typeId) {
data->idToType.insert(type->typeId, type);
@@ -477,14 +510,16 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe
bool fileImport = false;
if (*(type.uri) == '\0')
fileImport = true;
- if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, typeName))
+ if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri,
+ typeName, type.versionMajor)) {
return QQmlType();
+ }
QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
addTypeToData(priv, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
- files->insertMulti(QQmlTypeLoader::normalize(type.url), priv);
+ files->insert(QQmlTypeLoader::normalize(type.url), priv);
return QQmlType(priv);
}
@@ -505,17 +540,15 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit
addTypeToData(priv, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
- files->insertMulti(QQmlTypeLoader::normalize(type.url), priv);
+ files->insert(QQmlTypeLoader::normalize(type.url), priv);
return QQmlType(priv);
}
-void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
+CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className)
{
- QByteArray name = compilationUnit->rootPropertyCache()->className();
-
- QByteArray ptr = name + '*';
- QByteArray lst = "QQmlListProperty<" + name + '>';
+ QByteArray ptr = className + '*';
+ QByteArray lst = "QQmlListProperty<" + className + '>';
int ptr_type = QMetaType::registerNormalizedType(ptr,
QtMetaTypePrivate::QMetaTypeFunctionHelper<QObject*>::Destruct,
@@ -530,23 +563,19 @@ void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit
static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject> >::Flags),
static_cast<QMetaObject*>(nullptr));
- compilationUnit->metaTypeId = ptr_type;
- compilationUnit->listMetaTypeId = lst_type;
-
QQmlMetaTypeDataPtr data;
data->qmlLists.insert(lst_type, ptr_type);
+
+ return {ptr_type, lst_type};
}
-void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
+void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds)
{
- int ptr_type = compilationUnit->metaTypeId;
- int lst_type = compilationUnit->listMetaTypeId;
-
QQmlMetaTypeDataPtr data;
- data->qmlLists.remove(lst_type);
+ data->qmlLists.remove(typeIds.listId);
- QMetaType::unregisterType(ptr_type);
- QMetaType::unregisterType(lst_type);
+ QMetaType::unregisterType(typeIds.id);
+ QMetaType::unregisterType(typeIds.listId);
}
int QQmlMetaType::registerUnitCacheHook(
@@ -560,12 +589,12 @@ int QQmlMetaType::registerUnitCacheHook(
return 0;
}
-bool QQmlMetaType::protectModule(const char *uri, int majVersion)
+bool QQmlMetaType::protectModule(const QString &uri, int majVersion)
{
QQmlMetaTypeDataPtr data;
QQmlMetaTypeData::VersionedUri versionedUri;
- versionedUri.uri = QString::fromUtf8(uri);
+ versionedUri.uri = uri;
versionedUri.majorVersion = majVersion;
if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) {
@@ -642,17 +671,6 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
const QString &uri, const QString &typeNamespace, int vmaj,
QList<QQmlError> *errors)
{
- QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance);
- if (!iface) {
- if (errors) {
- QQmlError error;
- error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement "
- "QQmlTypesExtensionInterface").arg(typeNamespace));
- errors->prepend(error);
- }
- return false;
- }
-
if (!typeNamespace.isEmpty() && typeNamespace != uri) {
// This is an 'identified' module
// The namespace for type registrations must match the URI for locating the module
@@ -683,8 +701,6 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
}
return false;
}
-
- data->protectedNamespaces.insert(uri);
} else {
// This is not an identified module - provide a warning
qWarning().nospace() << qPrintable(
@@ -692,28 +708,42 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
"it cannot be protected from external registrations.").arg(uri));
}
- if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
- // basepath should point to the directory of the module, not the plugin file itself:
- QQmlExtensionPluginPrivate::get(plugin)->baseUrl
- = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
+ if (!qobject_cast<QQmlEngineExtensionInterface *>(instance)) {
+ QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance);
+ if (!iface) {
+ if (errors) {
+ QQmlError error;
+ // Also does not implement QQmlTypesExtensionInterface, but we want to discourage that.
+ error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement "
+ "QQmlEngineExtensionInterface").arg(typeNamespace));
+ errors->prepend(error);
+ }
+ return false;
+ }
+
+ if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
+ // basepath should point to the directory of the module, not the plugin file itself:
+ QQmlExtensionPluginPrivate::get(plugin)->baseUrl
+ = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
+ }
+
+ const QByteArray bytes = uri.toUtf8();
+ const char *moduleId = bytes.constData();
+ iface->registerTypes(moduleId);
}
- data->typeRegistrationNamespace = typeNamespace;
- const QByteArray bytes = uri.toUtf8();
- const char *moduleId = bytes.constData();
- iface->registerTypes(moduleId);
- data->typeRegistrationNamespace.clear();
- }
+ data->registerModuleTypes(QQmlMetaTypeData::VersionedUri(uri, vmaj));
- if (!failures.isEmpty()) {
- if (errors) {
- for (const QString &failure : qAsConst(failures)) {
- QQmlError error;
- error.setDescription(failure);
- errors->prepend(error);
+ if (!failures.isEmpty()) {
+ if (errors) {
+ for (const QString &failure : qAsConst(failures)) {
+ QQmlError error;
+ error.setDescription(failure);
+ errors->prepend(error);
+ }
}
+ return false;
}
- return false;
}
return true;
@@ -794,7 +824,7 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
data->registerType(priv);
addTypeToData(priv, data);
- data->urlToType.insertMulti(url, priv);
+ data->urlToType.insert(url, priv);
return QQmlType(priv);
}
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 6c2b0bb2a6..0c5bc043c4 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -63,6 +63,15 @@ class QQmlError;
namespace QV4 { class ExecutableCompilationUnit; }
+struct CompositeMetaTypeIds
+{
+ int id = -1;
+ int listId = -1;
+ CompositeMetaTypeIds() = default;
+ CompositeMetaTypeIds(int id, int listId) : id(id), listId(listId) {}
+ bool isValid() const { return id != -1 && listId != -1; }
+};
+
class Q_QML_PRIVATE_EXPORT QQmlMetaType
{
public:
@@ -80,11 +89,10 @@ public:
static void unregisterType(int type);
- static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
- static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
-
+ static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className);
+ static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds);
static void registerModule(const char *uri, int versionMajor, int versionMinor);
- static bool protectModule(const char *uri, int majVersion);
+ static bool protectModule(const QString &uri, int majVersion);
static int typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
@@ -188,6 +196,14 @@ public:
static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd);
+
+ static void qmlInsertModuleRegistration(const QString &uri, int majorVersion,
+ void (*registerFunction)());
+ static void qmlRemoveModuleRegistration(const QString &uri, int majorVersion);
+
+ static bool qmlRegisterModuleTypes(const QString &uri, int majorVersion);
+
+ static int qmlRegisteredListTypeCount();
};
Q_DECLARE_TYPEINFO(QQmlMetaType, Q_MOVABLE_TYPE);
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index 775bc8bdb4..ed885eaa97 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -78,6 +78,16 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
priv->release();
}
+bool QQmlMetaTypeData::registerModuleTypes(const QQmlMetaTypeData::VersionedUri &versionedUri)
+{
+ auto function = moduleTypeRegistrationFunctions.constFind(versionedUri);
+ if (function != moduleTypeRegistrationFunctions.constEnd()) {
+ (*function)();
+ return true;
+ }
+ return false;
+}
+
QQmlPropertyCache *QQmlMetaTypeData::propertyCacheForMinorVersion(int index, int minorVersion) const
{
return (index < typePropertyCaches.length())
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
index 5239b635ce..e51d4ca1a4 100644
--- a/src/qml/qml/qqmlmetatypedata_p.h
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -71,7 +71,7 @@ struct QQmlMetaTypeData
QSet<QQmlType> undeletableTypes;
typedef QHash<int, QQmlTypePrivate *> Ids;
Ids idToType;
- typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names;
+ typedef QMultiHash<QHashedStringRef, QQmlTypePrivate *> Names;
Names nameToType;
typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only
Files urlToType;
@@ -79,7 +79,7 @@ struct QQmlMetaTypeData
// singleton types. This way we can locate any
// of them by url, even if it was registered as
// a module via QQmlPrivate::RegisterCompositeType
- typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects;
+ typedef QMultiHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects;
MetaObjects metaObjectToType;
typedef QHash<int, QQmlMetaType::StringConverter> StringConverters;
StringConverters stringConverters;
@@ -96,9 +96,13 @@ struct QQmlMetaTypeData
QHashedString uri;
int majorVersion;
};
+
typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules;
TypeModules uriToModule;
+ QHash<VersionedUri, void (*)()> moduleTypeRegistrationFunctions;
+ bool registerModuleTypes(const VersionedUri &versionedUri);
+
QBitArray objects;
QBitArray interfaces;
QBitArray lists;
@@ -106,10 +110,6 @@ struct QQmlMetaTypeData
QList<QQmlPrivate::AutoParentFunction> parentFunctions;
QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit;
- QSet<QString> protectedNamespaces;
-
- QString typeRegistrationNamespace;
-
QHash<int, int> qmlLists;
QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches;
diff --git a/src/qml/qml/qqmlmoduleregistration.cpp b/src/qml/qml/qqmlmoduleregistration.cpp
new file mode 100644
index 0000000000..b7bc3555a6
--- /dev/null
+++ b/src/qml/qml/qqmlmoduleregistration.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module 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 <QtQml/private/qqmlmetatype_p.h>
+#include <QtQml/qqmlmoduleregistration.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlModuleRegistrationPrivate
+{
+ const QString uri;
+ const int majorVersion;
+};
+
+QQmlModuleRegistration::QQmlModuleRegistration(
+ const char *uri, int majorVersion,
+ void (*registerFunction)()) :
+ d(new QQmlModuleRegistrationPrivate { QString::fromUtf8(uri), majorVersion })
+{
+ QQmlMetaType::qmlInsertModuleRegistration(d->uri, d->majorVersion,
+ registerFunction);
+}
+
+QQmlModuleRegistration::~QQmlModuleRegistration()
+{
+ QQmlMetaType::qmlRemoveModuleRegistration(d->uri, d->majorVersion);
+ delete d;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmoduleregistration.h b/src/qml/qml/qqmlmoduleregistration.h
new file mode 100644
index 0000000000..6f553a2823
--- /dev/null
+++ b/src/qml/qml/qqmlmoduleregistration.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module 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 QQMLMODULEREGISTRATION_H
+#define QQMLMODULEREGISTRATION_H
+
+#include <QtQml/qtqmlglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlModuleRegistrationPrivate;
+class Q_QML_EXPORT QQmlModuleRegistration
+{
+ Q_DISABLE_COPY_MOVE(QQmlModuleRegistration)
+public:
+ QQmlModuleRegistration(const char *uri, int majorVersion, void (*registerFunction)());
+ ~QQmlModuleRegistration();
+
+private:
+ QQmlModuleRegistrationPrivate *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLMODULEREGISTRATION_H
diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp
index 1359586b31..df3731684a 100644
--- a/src/qml/qml/qqmlnotifier.cpp
+++ b/src/qml/qml/qqmlnotifier.cpp
@@ -118,8 +118,8 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine
disconnect();
Q_ASSERT(engine);
- if (QObjectPrivate::get(source)->threadData->threadId.loadRelaxed() !=
- QObjectPrivate::get(engine)->threadData->threadId.loadRelaxed()) {
+ if (QObjectPrivate::get(source)->threadData.loadRelaxed()->threadId.loadRelaxed() !=
+ QObjectPrivate::get(engine)->threadData.loadRelaxed()->threadId.loadRelaxed()) {
QString sourceName;
QDebug(&sourceName) << source;
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 836d27f245..3ca89924f9 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -56,25 +56,17 @@
#include <private/qqmldebugconnector_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmlscriptdata_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <private/qjsvalue_p.h>
#include <private/qv4generatorobject_p.h>
+#include <QScopedValueRollback>
+
#include <qtqml_tracepoints_p.h>
+#include <QScopedValueRollback>
QT_USE_NAMESPACE
-namespace {
-struct ActiveOCRestorer
-{
- ActiveOCRestorer(QQmlObjectCreator *creator, QQmlEnginePrivate *ep)
- : ep(ep), oldCreator(ep->activeObjectCreator) { ep->activeObjectCreator = creator; }
- ~ActiveOCRestorer() { ep->activeObjectCreator = oldCreator; }
-
- QQmlEnginePrivate *ep;
- QQmlObjectCreator *oldCreator;
-};
-}
-
QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext,
QQmlIncubatorPrivate *incubator)
: phase(Startup)
@@ -87,16 +79,17 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR
init(parentContext);
sharedState->componentAttached = nullptr;
- sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount);
- sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount);
- sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount);
+ sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount());
+ sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount());
+ sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount());
sharedState->allJavaScriptObjects = nullptr;
sharedState->creationContext = creationContext;
sharedState->rootContext = nullptr;
+ sharedState->hadRequiredProperties = false;
if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) {
Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler,
- sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount));
+ sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount()));
} else {
Q_UNUSED(profiler);
}
@@ -154,7 +147,7 @@ QQmlObjectCreator::~QQmlObjectCreator()
}
}
-QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt)
+QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt, int flags)
{
if (phase == CreatingObjectsPhase2) {
phase = ObjectsCreated;
@@ -168,8 +161,14 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
if (subComponentIndex == -1) {
objectToCreate = /*root object*/0;
} else {
- const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
- objectToCreate = compObj->bindingTable()->value.objectIndex;
+ Q_ASSERT(subComponentIndex >= 0);
+ if (flags & CreationFlags::InlineComponent) {
+ objectToCreate = subComponentIndex;
+ } else {
+ Q_ASSERT(flags & CreationFlags::NormalObject);
+ const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
+ objectToCreate = compObj->bindingTable()->value.objectIndex;
+ }
}
context = new QQmlContextData;
@@ -188,7 +187,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator);
if (topLevelCreator)
- sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount);
+ sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount());
if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) {
QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count()));
@@ -233,73 +232,23 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
return instance;
}
-// ### unify or keep in sync with populateDeferredBinding()
-bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData)
+void QQmlObjectCreator::beginPopulateDeferred(QQmlContextData *newContext)
{
- QQmlData *declarativeData = QQmlData::get(instance);
- context = deferredData->context;
- sharedState->rootContext = context;
-
- QObject *bindingTarget = instance;
-
- QQmlRefPointer<QQmlPropertyCache> cache = declarativeData->propertyCache;
- QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(instance);
-
- QObject *scopeObject = instance;
- qSwap(_scopeObject, scopeObject);
-
- QV4::Scope valueScope(v4);
+ context = newContext;
+ sharedState->rootContext = newContext;
Q_ASSERT(topLevelCreator);
Q_ASSERT(!sharedState->allJavaScriptObjects);
- sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
-
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
-
- qSwap(_qmlContext, qmlContext);
-
- qSwap(_propertyCache, cache);
- qSwap(_qobject, instance);
-
- int objectIndex = deferredData->deferredIdx;
- qSwap(_compiledObjectIndex, objectIndex);
-
- const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
- qSwap(_compiledObject, obj);
-
- qSwap(_ddata, declarativeData);
- qSwap(_bindingTarget, bindingTarget);
- qSwap(_vmeMetaObject, vmeMetaObject);
-
- setupBindings(/*applyDeferredBindings=*/true);
-
- qSwap(_vmeMetaObject, vmeMetaObject);
- qSwap(_bindingTarget, bindingTarget);
- qSwap(_ddata, declarativeData);
- qSwap(_compiledObject, obj);
- qSwap(_compiledObjectIndex, objectIndex);
- qSwap(_qobject, instance);
- qSwap(_propertyCache, cache);
-
- qSwap(_qmlContext, qmlContext);
- qSwap(_scopeObject, scopeObject);
- deferredData->bindings.clear();
- phase = ObjectsCreated;
-
- return errors.isEmpty();
+ QV4::Scope valueScope(v4);
+ sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount());
}
-// ### unify or keep in sync with populateDeferredProperties()
-bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, QQmlData::DeferredData *deferredData, const QV4::CompiledData::Binding *binding)
+void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
+ const QQmlPropertyPrivate *qmlProperty,
+ const QV4::CompiledData::Binding *binding)
{
- Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding);
-
- QObject *instance = qmlProperty.object();
QQmlData *declarativeData = QQmlData::get(instance);
- context = deferredData->context;
- sharedState->rootContext = context;
-
QObject *bindingTarget = instance;
QQmlRefPointer<QQmlPropertyCache> cache = declarativeData->propertyCache;
@@ -309,11 +258,10 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
qSwap(_scopeObject, scopeObject);
QV4::Scope valueScope(v4);
+ QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects,
+ valueScope.alloc(compilationUnit->totalObjectCount()));
Q_ASSERT(topLevelCreator);
- if (!sharedState->allJavaScriptObjects)
- sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
-
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -321,31 +269,37 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
qSwap(_propertyCache, cache);
qSwap(_qobject, instance);
- int objectIndex = deferredData->deferredIdx;
+ int objectIndex = deferredIndex;
qSwap(_compiledObjectIndex, objectIndex);
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
qSwap(_compiledObject, obj);
-
qSwap(_ddata, declarativeData);
qSwap(_bindingTarget, bindingTarget);
qSwap(_vmeMetaObject, vmeMetaObject);
- QQmlListProperty<void> savedList;
- qSwap(_currentList, savedList);
+ if (binding) {
+ Q_ASSERT(qmlProperty);
+ Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding);
- const QQmlPropertyData &property = QQmlPropertyPrivate::get(qmlProperty)->core;
+ QQmlListProperty<void> savedList;
+ qSwap(_currentList, savedList);
- if (property.isQList()) {
- void *argv[1] = { (void*)&_currentList };
- QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property.coreIndex(), argv);
- } else if (_currentList.object) {
- _currentList = QQmlListProperty<void>();
- }
+ const QQmlPropertyData &property = qmlProperty->core;
- setPropertyBinding(&property, binding);
+ if (property.isQList()) {
+ void *argv[1] = { (void*)&_currentList };
+ QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property.coreIndex(), argv);
+ } else if (_currentList.object) {
+ _currentList = QQmlListProperty<void>();
+ }
- qSwap(_currentList, savedList);
+ setPropertyBinding(&property, binding);
+
+ qSwap(_currentList, savedList);
+ } else {
+ setupBindings(/*applyDeferredBindings=*/true);
+ }
qSwap(_vmeMetaObject, vmeMetaObject);
qSwap(_bindingTarget, bindingTarget);
@@ -357,12 +311,29 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
qSwap(_qmlContext, qmlContext);
qSwap(_scopeObject, scopeObject);
+}
- phase = ObjectsCreated;
-
+bool QQmlObjectCreator::populateDeferredProperties(QObject *instance,
+ const QQmlData::DeferredData *deferredData)
+{
+ beginPopulateDeferred(deferredData->context);
+ populateDeferred(instance, deferredData->deferredIdx);
+ finalizePopulateDeferred();
return errors.isEmpty();
}
+void QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex,
+ const QV4::CompiledData::Binding *binding)
+{
+ populateDeferred(qmlProperty.object(), deferredIndex, QQmlPropertyPrivate::get(qmlProperty),
+ binding);
+}
+
+void QQmlObjectCreator::finalizePopulateDeferred()
+{
+ phase = ObjectsCreated;
+}
+
void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding)
{
QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite;
@@ -453,25 +424,25 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
}
break;
- case QVariant::String: {
+ case QMetaType::QString: {
assertOrNull(binding->evaluatesToString());
QString value = compilationUnit->bindingValueAsString(binding);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::StringList: {
+ case QMetaType::QStringList: {
assertOrNull(binding->evaluatesToString());
QStringList value(compilationUnit->bindingValueAsString(binding));
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::ByteArray: {
+ case QMetaType::QByteArray: {
assertType(QV4::CompiledData::Binding::Type_String);
QByteArray value(compilationUnit->bindingValueAsString(binding).toUtf8());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Url: {
+ case QMetaType::QUrl: {
assertType(QV4::CompiledData::Binding::Type_String);
QString string = compilationUnit->bindingValueAsString(binding);
// Encoded dir-separators defeat QUrl processing - decode them first
@@ -483,7 +454,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::UInt: {
+ case QMetaType::UInt: {
assertType(QV4::CompiledData::Binding::Type_Number);
double d = compilationUnit->bindingValueAsNumber(binding);
uint value = uint(d);
@@ -491,7 +462,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
}
break;
- case QVariant::Int: {
+ case QMetaType::Int: {
assertType(QV4::CompiledData::Binding::Type_Number);
double d = compilationUnit->bindingValueAsNumber(binding);
int value = int(d);
@@ -505,13 +476,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Double: {
+ case QMetaType::Double: {
assertType(QV4::CompiledData::Binding::Type_Number);
double value = compilationUnit->bindingValueAsNumber(binding);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Color: {
+ case QMetaType::QColor: {
bool ok = false;
uint colorValue = QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
@@ -522,21 +493,21 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
break;
#if QT_CONFIG(datestring)
- case QVariant::Date: {
+ case QMetaType::QDate: {
bool ok = false;
QDate value = QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Time: {
+ case QMetaType::QTime: {
bool ok = false;
QTime value = QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::DateTime: {
+ case QMetaType::QDateTime: {
bool ok = false;
QDateTime value = QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok);
// ### VME compatibility :(
@@ -550,55 +521,55 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
break;
#endif // datestring
- case QVariant::Point: {
+ case QMetaType::QPoint: {
bool ok = false;
QPoint value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok).toPoint();
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::PointF: {
+ case QMetaType::QPointF: {
bool ok = false;
QPointF value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Size: {
+ case QMetaType::QSize: {
bool ok = false;
QSize value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok).toSize();
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::SizeF: {
+ case QMetaType::QSizeF: {
bool ok = false;
QSizeF value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Rect: {
+ case QMetaType::QRect: {
bool ok = false;
QRect value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok).toRect();
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::RectF: {
+ case QMetaType::QRectF: {
bool ok = false;
QRectF value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Bool: {
+ case QMetaType::Bool: {
assertType(QV4::CompiledData::Binding::Type_Boolean);
bool value = binding->valueAsBoolean();
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
- case QVariant::Vector2D: {
+ case QMetaType::QVector2D: {
struct {
float xp;
float yp;
@@ -609,7 +580,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
- case QVariant::Vector3D: {
+ case QMetaType::QVector3D: {
struct {
float xp;
float yp;
@@ -621,7 +592,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
- case QVariant::Vector4D: {
+ case QMetaType::QVector4D: {
struct {
float xp;
float yp;
@@ -634,7 +605,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
- case QVariant::Quaternion: {
+ case QMetaType::QQuaternion: {
struct {
float wp;
float xp;
@@ -647,7 +618,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
- case QVariant::RegExp:
+ case QMetaType::QRegExp:
assertOrNull(!"not possible");
break;
default: {
@@ -712,7 +683,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
QVariant value = (*converter)(stringValue);
QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex());
- if (value.isNull() || ((int)metaProperty.type() != property->propType() && metaProperty.userType() != property->propType())) {
+ if (value.isNull() || metaProperty.userType() != property->propType()) {
recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name())));
break;
}
@@ -785,6 +756,23 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
+ QQmlPropertyData *const property = propertyData.at(i);
+ if (property) {
+ QQmlPropertyData* targetProperty = property;
+ if (targetProperty->isAlias()) {
+ // follow alias
+ auto target = _bindingTarget;
+ QQmlPropertyIndex originalIndex(targetProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1);
+ QQmlPropertyIndex propIndex;
+ QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
+ QQmlData *data = QQmlData::get(target);
+ Q_ASSERT(data && data->propertyCache);
+ targetProperty = data->propertyCache->property(propIndex.coreIndex());
+ }
+ sharedState->requiredProperties.remove(targetProperty);
+ }
+
+
if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding)
continue;
@@ -796,8 +784,6 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
continue;
}
- const QQmlPropertyData *property = propertyData.at(i);
-
if (property && property->isQList()) {
if (property->coreIndex() != currentListPropertyIndex) {
void *argv[1] = { (void*)&_currentList };
@@ -1150,8 +1136,8 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location,
{
QQmlError error;
error.setUrl(compilationUnit->url());
- error.setLine(location.line);
- error.setColumn(location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
error.setDescription(description);
errors << error;
}
@@ -1175,7 +1161,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QString typeName;
Q_TRACE_EXIT(QQmlObjectCreator_createInstance_exit, typeName);
- ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine));
+ QScopedValueRollback<QQmlObjectCreator*> ocRestore(QQmlEnginePrivate::get(engine)->activeObjectCreator, this);
bool isComponent = false;
QObject *instance = nullptr;
@@ -1196,7 +1182,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
Q_ASSERT(typeRef);
installPropertyCache = !typeRef->isFullyDynamicType;
QQmlType type = typeRef->type;
- if (type.isValid()) {
+ if (type.isValid() && !type.isInlineComponentType()) {
typeName = type.qmlTypeName();
void *ddataMemory = nullptr;
@@ -1228,19 +1214,34 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
sharedState->allCreatedObjects.push(instance);
} else {
- Q_ASSERT(typeRef->compilationUnit);
- typeName = typeRef->compilationUnit->fileName();
- if (typeRef->compilationUnit->unitData()->isSingleton())
+ const auto compilationUnit = typeRef->compilationUnit();
+ Q_ASSERT(compilationUnit);
+ typeName = compilationUnit->fileName();
+ // compilation unit is shared between root type and its inline component types
+ // so isSingleton errorneously returns true for inline components
+ if (compilationUnit->unitData()->isSingleton() && !type.isInlineComponentType())
{
recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex)));
return nullptr;
}
- QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data());
- instance = subCreator.create();
- if (!instance) {
- errors += subCreator.errors;
- return nullptr;
+
+ if (!type.isInlineComponentType()) {
+ QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data());
+ instance = subCreator.create();
+ if (!instance) {
+ errors += subCreator.errors;
+ return nullptr;
+ }
+ } else {
+ int subObjectId = type.inlineComponendId();
+ QScopedValueRollback<int> rollback {compilationUnit->icRoot, subObjectId};
+ QQmlObjectCreator subCreator(context, compilationUnit, sharedState.data());
+ instance = subCreator.create(subObjectId, nullptr, nullptr, CreationFlags::InlineComponent);
+ if (!instance) {
+ errors += subCreator.errors;
+ return nullptr;
+ }
}
}
if (instance->isWidgetType()) {
@@ -1265,7 +1266,10 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
ddata->columnNumber = obj->location.column;
ddata->setImplicitDestructible();
- if (static_cast<quint32>(index) == /*root object*/0 || ddata->rootObjectInCreation) {
+ // inline components are root objects, but their index is != 0, so we need
+ // an additional check
+ const bool isInlineComponent = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot;
+ if (static_cast<quint32>(index) == /*root object*/0 || ddata->rootObjectInCreation || isInlineComponent) {
if (ddata->context) {
Q_ASSERT(ddata->context != context);
Q_ASSERT(ddata->outerContext);
@@ -1376,7 +1380,7 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru
phase = Finalizing;
QQmlObjectCreatorRecursionWatcher watcher(this);
- ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine));
+ QScopedValueRollback<QQmlObjectCreator*> ocRestore(QQmlEnginePrivate::get(engine)->activeObjectCreator, this);
while (!sharedState->allCreatedBindings.isEmpty()) {
QQmlAbstractBinding::Ptr b = sharedState->allCreatedBindings.pop();
@@ -1506,10 +1510,73 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings)
_ddata->deferData(_compiledObjectIndex, compilationUnit, context);
+ QSet<QString> postHocRequired;
+ for (auto it = _compiledObject->requiredPropertyExtraDataBegin(); it != _compiledObject->requiredPropertyExtraDataEnd(); ++it)
+ postHocRequired.insert(stringAt(it->nameIndex));
+ bool hadInheritedRequiredProperties = !postHocRequired.empty();
+
+ for (int propertyIndex = 0; propertyIndex != _compiledObject->propertyCount(); ++propertyIndex) {
+ const QV4::CompiledData::Property* property = _compiledObject->propertiesBegin() + propertyIndex;
+ QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex);
+ // only compute stringAt if there's a chance for the lookup to succeed
+ auto postHocIt = postHocRequired.isEmpty() ? postHocRequired.end() : postHocRequired.find(stringAt(property->nameIndex));
+ if (!property->isRequired && postHocRequired.end() == postHocIt)
+ continue;
+ if (postHocIt != postHocRequired.end())
+ postHocRequired.erase(postHocIt);
+ sharedState->hadRequiredProperties = true;
+ sharedState->requiredProperties.insert(propertyData,
+ RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex), compilationUnit->finalUrl(), property->location, {}});
+
+ }
+
+ for (int i = 0; i <= _propertyCache->propertyOffset(); ++i) {
+ QQmlPropertyData *propertyData = _propertyCache->maybeUnresolvedProperty(i);
+ if (!propertyData)
+ continue;
+ if (!propertyData->isRequired() && postHocRequired.isEmpty())
+ continue;
+ QString name = propertyData->name(_qobject);
+ auto postHocIt = postHocRequired.find(name);
+ if (!propertyData->isRequired() && postHocRequired.end() == postHocIt )
+ continue;
+
+ if (postHocIt != postHocRequired.end())
+ postHocRequired.erase(postHocIt);
+
+ sharedState->hadRequiredProperties = true;
+ sharedState->requiredProperties.insert(propertyData, RequiredPropertyInfo {name, compilationUnit->finalUrl(), _compiledObject->location, {}});
+ }
+ if (!postHocRequired.isEmpty() && hadInheritedRequiredProperties)
+ recordError({}, QLatin1String("Property %1 was marked as required but does not exist").arg(*postHocRequired.begin()));
+
if (_compiledObject->nFunctions > 0)
setupFunctions();
setupBindings();
+ for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) {
+ const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex;
+ const auto originalAlias = alias;
+ while (alias->aliasToLocalAlias)
+ alias = _compiledObject->aliasesBegin() + alias->localAliasIndex;
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+ if (!context->idValues->wasSet())
+ continue;
+ QObject *target = context->idValues[alias->targetObjectId].data();
+ if (!target)
+ continue;
+ QQmlData *targetDData = QQmlData::get(target, /*create*/false);
+ if (!targetDData)
+ continue;
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+ QQmlPropertyData *const targetProperty = targetDData->propertyCache->property(coreIndex);
+ if (!targetProperty)
+ continue;
+ auto it = sharedState->requiredProperties.find(targetProperty);
+ if (it != sharedState->requiredProperties.end())
+ it->aliasesToRequired.push_back(AliasToRequiredInfo {compilationUnit->stringAt(originalAlias->nameIndex), compilationUnit->finalUrl()});
+ }
+
qSwap(_vmeMetaObject, vmeMetaObject);
qSwap(_bindingTarget, bindingTarget);
qSwap(_ddata, declarativeData);
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index ecdbcc56dd..bca450addb 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -66,6 +66,28 @@ class QQmlAbstractBinding;
class QQmlInstantiationInterrupt;
class QQmlIncubatorPrivate;
+struct AliasToRequiredInfo {
+ QString propertyName;
+ QUrl fileUrl;
+};
+
+/*!
+\internal
+This struct contains information solely used for displaying error messages
+\variable aliasesToRequired allows us to give the user a way to know which (aliasing) properties
+can be set to set the required property
+\sa QQmlComponentPrivate::unsetRequiredPropertyToQQmlError
+*/
+struct RequiredPropertyInfo
+{
+ QString propertyName;
+ QUrl fileUrl;
+ QV4::CompiledData::Location location;
+ QVector<AliasToRequiredInfo> aliasesToRequired;
+};
+
+class RequiredProperties : public QHash<QQmlPropertyData*, RequiredPropertyInfo> {};
+
struct QQmlObjectCreatorSharedState : public QSharedData
{
QQmlContextData *rootContext;
@@ -78,6 +100,8 @@ struct QQmlObjectCreatorSharedState : public QSharedData
QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks;
QQmlVmeProfiler profiler;
QRecursionNode recursionNode;
+ RequiredProperties requiredProperties;
+ bool hadRequiredProperties;
};
class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
@@ -87,9 +111,16 @@ public:
QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
~QQmlObjectCreator();
- QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr);
- bool populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData);
- bool populateDeferredBinding(const QQmlProperty &qmlProperty, QQmlData::DeferredData *deferredData, const QV4::CompiledData::Binding *binding);
+ enum CreationFlags { NormalObject = 1, InlineComponent = 2 };
+ QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr, int flags = NormalObject);
+
+ bool populateDeferredProperties(QObject *instance, const QQmlData::DeferredData *deferredData);
+
+ void beginPopulateDeferred(QQmlContextData *context);
+ void populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex,
+ const QV4::CompiledData::Binding *binding);
+ void finalizePopulateDeferred();
+
QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt);
void clear();
@@ -102,6 +133,9 @@ public:
QQmlContextData *parentContextData() const { return parentContext.contextData(); }
QFiniteStack<QPointer<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; }
+ RequiredProperties &requiredProperties() {return sharedState->requiredProperties;}
+ bool componentHadRequiredProperties() const {return sharedState->hadRequiredProperties;}
+
private:
QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
@@ -112,6 +146,11 @@ private:
bool populateInstance(int index, QObject *instance,
QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty);
+ // If qmlProperty and binding are null, populate all properties, otherwise only the given one.
+ void populateDeferred(QObject *instance, int deferredIndex,
+ const QQmlPropertyPrivate *qmlProperty = nullptr,
+ const QV4::CompiledData::Binding *binding = nullptr);
+
void setupBindings(bool applyDeferredBindings = false);
bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp
index 0acf20bbb4..dcd7ca2d46 100644
--- a/src/qml/qml/qqmlplatform.cpp
+++ b/src/qml/qml/qqmlplatform.cpp
@@ -74,6 +74,8 @@ QString QQmlPlatform::os()
return QStringLiteral("linux");
#elif defined(Q_OS_QNX)
return QStringLiteral("qnx");
+#elif defined(Q_OS_WASM)
+ return QStringLiteral("wasm");
#elif defined(Q_OS_UNIX)
return QStringLiteral("unix");
#else
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 6d7a2569bc..d6a9e73763 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -52,18 +52,43 @@
//
#include <functional>
+#include <type_traits>
#include <QtQml/qtqmlglobal.h>
+#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqmlpropertyvaluesource.h>
#include <QtCore/qglobal.h>
#include <QtCore/qvariant.h>
#include <QtCore/qurl.h>
#include <QtCore/qpointer.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qdebug.h>
+
+#define QML_GETTYPENAMES \
+ const char *className = T::staticMetaObject.className(); \
+ const int nameLen = int(strlen(className)); \
+ QVarLengthArray<char,48> pointerName(nameLen+2); \
+ memcpy(pointerName.data(), className, size_t(nameLen)); \
+ pointerName[nameLen] = '*'; \
+ pointerName[nameLen+1] = '\0'; \
+ const int listLen = int(strlen("QQmlListProperty<")); \
+ QVarLengthArray<char,64> listName(listLen + nameLen + 2); \
+ memcpy(listName.data(), "QQmlListProperty<", size_t(listLen)); \
+ memcpy(listName.data()+listLen, className, size_t(nameLen)); \
+ listName[listLen+nameLen] = '>'; \
+ listName[listLen+nameLen+1] = '\0';
+
QT_BEGIN_NAMESPACE
+class QQmlPropertyValueInterceptor;
+
namespace QQmlPrivate {
struct CachedQmlUnit;
+template<typename A>
+using QQmlAttachedPropertiesFunc = A *(*)(QObject *);
}
namespace QV4 {
@@ -78,7 +103,7 @@ struct Document;
typedef void (*IRLoaderFunction)(Document *, const QQmlPrivate::CachedQmlUnit *);
}
-typedef QObject *(*QQmlAttachedPropertiesFunc)(QObject *);
+using QQmlAttachedPropertiesFunc = QQmlPrivate::QQmlAttachedPropertiesFunc<QObject>;
inline uint qHash(QQmlAttachedPropertiesFunc func, uint seed = 0)
{
@@ -99,6 +124,14 @@ class QJSValue;
class QJSEngine;
class QQmlEngine;
class QQmlCustomParser;
+class QQmlTypeNotAvailable;
+
+template<class T>
+QQmlCustomParser *qmlCreateCustomParser()
+{
+ return nullptr;
+}
+
namespace QQmlPrivate
{
void Q_QML_EXPORT qdeclarativeelement_destructor(QObject *);
@@ -124,11 +157,62 @@ namespace QQmlPrivate
};
template<typename T>
+ constexpr bool isConstructible()
+ {
+ return std::is_default_constructible<T>::value && std::is_base_of<QObject, T>::value;
+ }
+
+ template<typename T>
void createInto(void *memory) { new (memory) QQmlElement<T>; }
template<typename T>
+ QObject *createSingletonInstance(QQmlEngine *, QJSEngine *) { return new T; }
+
+ template<typename T>
QObject *createParent(QObject *p) { return new T(p); }
+ using CreateIntoFunction = void (*)(void *);
+ using CreateSingletonFunction = QObject *(*)(QQmlEngine *, QJSEngine *);
+ using CreateParentFunction = QObject *(*)(QObject *);
+
+ template<typename T, bool Constructible = isConstructible<T>()>
+ struct Constructors;
+
+ template<typename T>
+ struct Constructors<T, true>
+ {
+ static constexpr CreateIntoFunction createInto
+ = QQmlPrivate::createInto<T>;
+ static constexpr CreateSingletonFunction createSingletonInstance
+ = QQmlPrivate::createSingletonInstance<T>;
+ };
+
+ template<typename T>
+ struct Constructors<T, false>
+ {
+ static constexpr CreateIntoFunction createInto = nullptr;
+ static constexpr CreateSingletonFunction createSingletonInstance = nullptr;
+ };
+
+ template<typename T, bool IsVoid = std::is_void<T>::value>
+ struct ExtendedType;
+
+ // void means "not an extended type"
+ template<typename T>
+ struct ExtendedType<T, true>
+ {
+ static constexpr const CreateParentFunction createParent = nullptr;
+ static constexpr const QMetaObject *staticMetaObject = nullptr;
+ };
+
+ // If it's not void, we actually want an error if the ctor or the metaobject is missing.
+ template<typename T>
+ struct ExtendedType<T, false>
+ {
+ static constexpr const CreateParentFunction createParent = QQmlPrivate::createParent<T>;
+ static constexpr const QMetaObject *staticMetaObject = &T::staticMetaObject;
+ };
+
template<class From, class To, int N>
struct StaticCastSelectorClass
{
@@ -156,66 +240,103 @@ namespace QQmlPrivate
}
};
- template <typename T>
- struct has_attachedPropertiesMember
+ template<typename...>
+ using QmlVoidT = void;
+
+ // You can prevent subclasses from using the same attached type by specialzing this.
+ // This is reserved for internal types, though.
+ template<class T, class A>
+ struct OverridableAttachedType
{
- static bool const value = QQmlTypeInfo<T>::hasAttachedProperties;
+ using Type = A;
};
- template <typename T, bool hasMember>
- class has_attachedPropertiesMethod
+ template<class T, class = QmlVoidT<>, bool OldStyle = QQmlTypeInfo<T>::hasAttachedProperties>
+ struct QmlAttached
{
- public:
- typedef int yes_type;
- typedef char no_type;
-
- template<typename ReturnType>
- static yes_type checkType(ReturnType *(*)(QObject *));
- static no_type checkType(...);
-
- static bool const value = sizeof(checkType(&T::qmlAttachedProperties)) == sizeof(yes_type);
+ using Type = void;
+ using Func = QQmlAttachedPropertiesFunc<QObject>;
+ static const QMetaObject *staticMetaObject() { return nullptr; }
+ static Func attachedPropertiesFunc() { return nullptr; }
};
- template <typename T>
- class has_attachedPropertiesMethod<T, false>
+ // Defined inline via QML_ATTACHED
+ template<class T>
+ struct QmlAttached<T, QmlVoidT<typename OverridableAttachedType<T, typename T::QmlAttachedType>::Type>, false>
{
- public:
- static bool const value = false;
+ // Normal attached properties
+ template <typename Parent, typename Attached>
+ struct Properties
+ {
+ using Func = QQmlAttachedPropertiesFunc<Attached>;
+ static const QMetaObject *staticMetaObject() { return &Attached::staticMetaObject; }
+ static Func attachedPropertiesFunc() { return Parent::qmlAttachedProperties; }
+ };
+
+ // Disabled via OverridableAttachedType
+ template<typename Parent>
+ struct Properties<Parent, void>
+ {
+ using Func = QQmlAttachedPropertiesFunc<QObject>;
+ static const QMetaObject *staticMetaObject() { return nullptr; };
+ static Func attachedPropertiesFunc() { return nullptr; };
+ };
+
+ using Type = typename OverridableAttachedType<T, typename T::QmlAttachedType>::Type;
+ using Func = typename Properties<T, Type>::Func;
+
+ static const QMetaObject *staticMetaObject()
+ {
+ return Properties<T, Type>::staticMetaObject();
+ }
+
+ static Func attachedPropertiesFunc()
+ {
+ return Properties<T, Type>::attachedPropertiesFunc();
+ }
};
- template<typename T, int N>
- class AttachedPropertySelector
+ // Separately defined via QQmlTypeInfo
+ template<class T>
+ struct QmlAttached<T, QmlVoidT<decltype(T::qmlAttachedProperties)>, true>
{
- public:
- static inline QQmlAttachedPropertiesFunc func() { return nullptr; }
- static inline const QMetaObject *metaObject() { return nullptr; }
+ using Type = typename std::remove_pointer<decltype(T::qmlAttachedProperties(nullptr))>::type;
+ using Func = QQmlAttachedPropertiesFunc<Type>;
+
+ static const QMetaObject *staticMetaObject() { return &Type::staticMetaObject; }
+ static Func attachedPropertiesFunc() { return T::qmlAttachedProperties; }
};
- template<typename T>
- class AttachedPropertySelector<T, 1>
+
+ // This is necessary because both the type containing a default template parameter and the type
+ // instantiating the template need to have access to the default template parameter type. In
+ // this case that's T::QmlAttachedType. The QML_FOREIGN macro needs to befriend specific other
+ // types. Therefore we need some kind of "accessor". Because of compiler bugs in gcc and clang,
+ // we cannot befriend attachedPropertiesFunc() directly. Wrapping the actual access into another
+ // struct "fixes" that. For convenience we still want the free standing functions in addition.
+ template<class T>
+ struct QmlAttachedAccessor
{
- template<typename ReturnType>
- static inline const QMetaObject *attachedPropertiesMetaObject(ReturnType *(*)(QObject *)) {
- return &ReturnType::staticMetaObject;
- }
- public:
- static inline QQmlAttachedPropertiesFunc func() {
- return QQmlAttachedPropertiesFunc(&T::qmlAttachedProperties);
+ static QQmlAttachedPropertiesFunc<QObject> attachedPropertiesFunc()
+ {
+ return QQmlAttachedPropertiesFunc<QObject>(QmlAttached<T>::attachedPropertiesFunc());
}
- static inline const QMetaObject *metaObject() {
- return attachedPropertiesMetaObject(&T::qmlAttachedProperties);
+
+ static const QMetaObject *staticMetaObject()
+ {
+ return QmlAttached<T>::staticMetaObject();
}
};
template<typename T>
- inline QQmlAttachedPropertiesFunc attachedPropertiesFunc()
+ inline QQmlAttachedPropertiesFunc<QObject> attachedPropertiesFunc()
{
- return AttachedPropertySelector<T, has_attachedPropertiesMethod<T, has_attachedPropertiesMember<T>::value>::value>::func();
+ return QmlAttachedAccessor<T>::attachedPropertiesFunc();
}
template<typename T>
inline const QMetaObject *attachedPropertiesMetaObject()
{
- return AttachedPropertySelector<T, has_attachedPropertiesMethod<T, has_attachedPropertiesMember<T>::value>::value>::metaObject();
+ return QmlAttachedAccessor<T>::staticMetaObject();
}
enum AutoParentResult { Parented, IncompatibleObject, IncompatibleParent };
@@ -236,7 +357,7 @@ namespace QQmlPrivate
const char *elementName;
const QMetaObject *metaObject;
- QQmlAttachedPropertiesFunc attachedPropertiesFunction;
+ QQmlAttachedPropertiesFunc<QObject> attachedPropertiesFunction;
const QMetaObject *attachedPropertiesMetaObject;
int parserStatusCast;
@@ -247,10 +368,38 @@ namespace QQmlPrivate
const QMetaObject *extensionMetaObject;
QQmlCustomParser *customParser;
+
int revision;
// If this is extended ensure "version" is bumped!!!
};
+ struct RegisterTypeAndRevisions {
+ int version;
+
+ int typeId;
+ int listId;
+ int objectSize;
+ void (*create)(void *);
+
+ const char *uri;
+ int versionMajor;
+
+ const QMetaObject *metaObject;
+ const QMetaObject *classInfoMetaObject;
+
+ QQmlAttachedPropertiesFunc<QObject> attachedPropertiesFunction;
+ const QMetaObject *attachedPropertiesMetaObject;
+
+ int parserStatusCast;
+ int valueSourceCast;
+ int valueInterceptorCast;
+
+ QObject *(*extensionObjectCreate)(QObject *);
+ const QMetaObject *extensionMetaObject;
+
+ QQmlCustomParser *(*customParserFactory)();
+ };
+
struct RegisterInterface {
int version;
@@ -258,6 +407,9 @@ namespace QQmlPrivate
int listId;
const char *iid;
+
+ const char *uri;
+ int versionMajor;
};
struct RegisterAutoParent {
@@ -283,6 +435,19 @@ namespace QQmlPrivate
// If this is extended ensure "version" is bumped!!!
};
+ struct RegisterSingletonTypeAndRevisions {
+ int version;
+ const char *uri;
+ int versionMajor;
+
+ QJSValue (*scriptApi)(QQmlEngine *, QJSEngine *);
+ const QMetaObject *instanceMetaObject;
+ const QMetaObject *classInfoMetaObject;
+
+ int typeId;
+ std::function<QObject*(QQmlEngine *, QJSEngine *)> generalizedQobjectApi; // new in version 3
+ };
+
struct RegisterCompositeType {
QUrl url;
const char *uri;
@@ -318,7 +483,9 @@ namespace QQmlPrivate
SingletonRegistration = 3,
CompositeRegistration = 4,
CompositeSingletonRegistration = 5,
- QmlUnitCacheHookRegistration = 6
+ QmlUnitCacheHookRegistration = 6,
+ TypeAndRevisionsRegistration = 7,
+ SingletonAndRevisionsRegistration = 8
};
int Q_QML_EXPORT qmlregister(RegistrationType, void *);
@@ -330,7 +497,168 @@ namespace QQmlPrivate
QPointer<QObject> m_object;
bool alreadyCalled = false;
};
-}
+
+ static int indexOfOwnClassInfo(const QMetaObject *metaObject, const char *key)
+ {
+ if (!metaObject || !key)
+ return -1;
+
+ const int offset = metaObject->classInfoOffset();
+ for (int i = metaObject->classInfoCount() + offset - 1; i >= offset; --i)
+ if (qstrcmp(key, metaObject->classInfo(i).name()) == 0) {
+ return i;
+ }
+ return -1;
+ }
+
+ inline const char *classInfo(const QMetaObject *metaObject, const char *key)
+ {
+ return metaObject->classInfo(indexOfOwnClassInfo(metaObject, key)).value();
+ }
+
+ inline int intClassInfo(const QMetaObject *metaObject, const char *key, int defaultValue = 0)
+ {
+ const int index = indexOfOwnClassInfo(metaObject, key);
+ return (index == -1) ? defaultValue
+ : QByteArray(metaObject->classInfo(index).value()).toInt();
+ }
+
+ inline bool boolClassInfo(const QMetaObject *metaObject, const char *key,
+ bool defaultValue = false)
+ {
+ const int index = indexOfOwnClassInfo(metaObject, key);
+ return (index == -1) ? defaultValue
+ : (QByteArray(metaObject->classInfo(index).value()) == "true");
+ }
+
+ inline const char *classElementName(const QMetaObject *metaObject)
+ {
+ const char *elementName = classInfo(metaObject, "QML.Element");
+ if (qstrcmp(elementName, "auto") == 0)
+ return metaObject->className();
+ if (qstrcmp(elementName, "anonymous") == 0)
+ return nullptr;
+
+ if (!elementName || elementName[0] < 'A' || elementName[0] > 'Z') {
+ qWarning() << "Missing or unusable QML.Element class info \"" << elementName << "\""
+ << "for" << metaObject->className();
+ }
+
+ return elementName;
+ }
+
+ template<class T, class = QmlVoidT<>>
+ struct QmlExtended
+ {
+ using Type = void;
+ };
+
+ template<class T>
+ struct QmlExtended<T, QmlVoidT<typename T::QmlExtendedType>>
+ {
+ using Type = typename T::QmlExtendedType;
+ };
+
+ template<class T, class = QmlVoidT<>>
+ struct QmlResolved
+ {
+ using Type = T;
+ };
+
+ template<class T>
+ struct QmlResolved<T, QmlVoidT<typename T::QmlForeignType>>
+ {
+ using Type = typename T::QmlForeignType;
+ };
+
+ template<class T, class = QmlVoidT<>>
+ struct QmlSingleton
+ {
+ static constexpr bool Value = false;
+ };
+
+ template<class T>
+ struct QmlSingleton<T, QmlVoidT<typename T::QmlIsSingleton>>
+ {
+ static constexpr bool Value = bool(T::QmlIsSingleton::yes);
+ };
+
+ template<class T, class = QmlVoidT<>>
+ struct QmlInterface
+ {
+ static constexpr bool Value = false;
+ };
+
+ template<class T>
+ struct QmlInterface<T, QmlVoidT<typename T::QmlIsInterface>>
+ {
+ static constexpr bool Value = bool(T::QmlIsInterface::yes);
+ };
+
+ template<typename T>
+ void qmlRegisterSingletonAndRevisions(const char *uri, int versionMajor,
+ const QMetaObject *classInfoMetaObject)
+ {
+ QML_GETTYPENAMES
+
+ RegisterSingletonTypeAndRevisions api = {
+ 0,
+
+ uri,
+ versionMajor,
+
+ nullptr,
+
+ &T::staticMetaObject,
+ classInfoMetaObject,
+
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ Constructors<T>::createSingletonInstance
+ };
+
+ qmlregister(SingletonAndRevisionsRegistration, &api);
+ }
+
+ template<typename T, typename E>
+ void qmlRegisterTypeAndRevisions(const char *uri, int versionMajor,
+ const QMetaObject *classInfoMetaObject)
+ {
+ QML_GETTYPENAMES
+
+ RegisterTypeAndRevisions type = {
+ 0,
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
+ int(sizeof(T)),
+ Constructors<T>::createInto,
+
+ uri,
+ versionMajor,
+
+ &T::staticMetaObject,
+ classInfoMetaObject,
+
+ attachedPropertiesFunc<T>(),
+ attachedPropertiesMetaObject<T>(),
+
+ StaticCastSelector<T, QQmlParserStatus>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
+
+ ExtendedType<E>::createParent,
+ ExtendedType<E>::staticMetaObject,
+
+ &qmlCreateCustomParser<T>
+ };
+
+ qmlregister(TypeAndRevisionsRegistration, &type);
+ }
+
+ template<>
+ void Q_QML_EXPORT qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
+ const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject);
+
+} // namespace QQmlPrivate
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 86cb606f83..8521de6ab3 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -476,7 +476,7 @@ QQmlPropertyPrivate::propertyTypeCategory() const
return QQmlProperty::Normal;
} else if (type & QQmlProperty::Property) {
int type = propertyType();
- if (type == QVariant::Invalid)
+ if (type == QMetaType::UnknownType)
return QQmlProperty::InvalidCategory;
else if (QQmlValueTypeFactory::isValueType((uint)type))
return QQmlProperty::Normal;
@@ -531,7 +531,7 @@ bool QQmlProperty::operator==(const QQmlProperty &other) const
*/
int QQmlProperty::propertyType() const
{
- return d ? d->propertyType() : int(QVariant::Invalid);
+ return d ? d->propertyType() : int(QMetaType::UnknownType);
}
bool QQmlPropertyPrivate::isValueType() const
@@ -547,7 +547,7 @@ int QQmlPropertyPrivate::propertyType() const
} else if (type & QQmlProperty::Property) {
return core.propType();
} else {
- return QVariant::Invalid;
+ return QMetaType::UnknownType;
}
}
@@ -790,7 +790,7 @@ static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPrope
return;
if (!(flags & QQmlPropertyPrivate::DontEnable))
- oldBinding->setEnabled(false, nullptr);
+ oldBinding->setEnabled(false, {});
oldBinding->removeFromObject();
}
@@ -896,7 +896,7 @@ void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags
QQmlData *data = QQmlData::get(object, true);
if (data->propertyCache) {
QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
- Q_ASSERT(propertyData && !propertyData->isAlias());
+ Q_ASSERT(propertyData);
}
#endif
@@ -1144,7 +1144,7 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
QVariant v = value;
if (prop.isEnumType()) {
QMetaEnum menum = prop.enumerator();
- if (v.userType() == QVariant::String
+ if (v.userType() == QMetaType::QString
#ifdef QT3_SUPPORT
|| v.userType() == QVariant::CString
#endif
@@ -1156,13 +1156,13 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
if (!ok)
return false;
- } else if (v.userType() != QVariant::Int && v.userType() != QVariant::UInt) {
+ } else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) {
int enumMetaTypeId = QMetaType::type(QByteArray(menum.scope() + QByteArray("::") + menum.name()));
if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData())
return false;
v = QVariant(*reinterpret_cast<const int *>(v.constData()));
}
- v.convert(QVariant::Int);
+ v.convert(QMetaType::Int);
}
// the status variable is changed by qt_metacall to indicate what it did
@@ -1229,17 +1229,17 @@ bool QQmlPropertyPrivate::write(QObject *object,
QMetaProperty prop = object->metaObject()->property(property.coreIndex());
QVariant v = value;
// Enum values come through the script engine as doubles
- if (variantType == QVariant::Double) {
+ if (variantType == QMetaType::Double) {
double integral;
double fractional = std::modf(value.toDouble(), &integral);
if (qFuzzyIsNull(fractional))
- v.convert(QVariant::Int);
+ v.convert(QMetaType::Int);
}
return writeEnumProperty(prop, property.coreIndex(), object, v, flags);
}
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context);
- const bool isUrl = propertyType == QVariant::Url; // handled separately
+ const bool isUrl = propertyType == QMetaType::QUrl; // handled separately
// The cases below are in approximate order of likelyhood:
if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
@@ -1272,7 +1272,7 @@ bool QQmlPropertyPrivate::write(QObject *object,
} else {
return false;
}
- } else if (value.canConvert(propertyType) && !isUrl && variantType != QVariant::String && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
+ } else if (value.canConvert(propertyType) && !isUrl && variantType != QMetaType::QString && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
// common cases:
switch (propertyType) {
case QMetaType::Bool: {
@@ -1305,14 +1305,14 @@ bool QQmlPropertyPrivate::write(QObject *object,
return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
} else if (isUrl) {
QUrl u;
- if (variantType == QVariant::Url) {
+ if (variantType == QMetaType::QUrl) {
u = value.toUrl();
- } else if (variantType == QVariant::ByteArray) {
+ } else if (variantType == QMetaType::QByteArray) {
QString input(QString::fromUtf8(value.toByteArray()));
// Encoded dir-separators defeat QUrl processing - decode them first
input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
u = QUrl(input);
- } else if (variantType == QVariant::String) {
+ } else if (variantType == QMetaType::QString) {
QString input(value.toString());
// Encoded dir-separators defeat QUrl processing - decode them first
input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
@@ -1378,7 +1378,7 @@ bool QQmlPropertyPrivate::write(QObject *object,
bool ok = false;
QVariant v;
- if (variantType == QVariant::String)
+ if (variantType == QMetaType::QString)
v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok);
if (!ok) {
@@ -1391,8 +1391,8 @@ bool QQmlPropertyPrivate::write(QObject *object,
// successful conversion.
Q_ASSERT(v.userType() == propertyType);
ok = true;
- } else if (static_cast<uint>(propertyType) >= QVariant::UserType &&
- variantType == QVariant::String) {
+ } else if (static_cast<uint>(propertyType) >= QMetaType::User &&
+ variantType == QMetaType::QString) {
QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType);
if (con) {
v = con(value.toString());
@@ -1406,28 +1406,28 @@ bool QQmlPropertyPrivate::write(QObject *object,
// to a sequence type property (eg, an int to a QList<int> property).
// or that we encountered an interface type
// Note that we've already handled single-value assignment to QList<QUrl> properties.
- if (variantType == QVariant::Int && propertyType == qMetaTypeId<QList<int> >()) {
+ if (variantType == QMetaType::Int && propertyType == qMetaTypeId<QList<int> >()) {
QList<int> list;
list << value.toInt();
v = QVariant::fromValue<QList<int> >(list);
ok = true;
- } else if ((variantType == QVariant::Double || variantType == QVariant::Int)
+ } else if ((variantType == QMetaType::Double || variantType == QMetaType::Int)
&& (propertyType == qMetaTypeId<QList<qreal> >())) {
QList<qreal> list;
list << value.toReal();
v = QVariant::fromValue<QList<qreal> >(list);
ok = true;
- } else if (variantType == QVariant::Bool && propertyType == qMetaTypeId<QList<bool> >()) {
+ } else if (variantType == QMetaType::Bool && propertyType == qMetaTypeId<QList<bool> >()) {
QList<bool> list;
list << value.toBool();
v = QVariant::fromValue<QList<bool> >(list);
ok = true;
- } else if (variantType == QVariant::String && propertyType == qMetaTypeId<QList<QString> >()) {
+ } else if (variantType == QMetaType::QString && propertyType == qMetaTypeId<QList<QString> >()) {
QList<QString> list;
list << value.toString();
v = QVariant::fromValue<QList<QString> >(list);
ok = true;
- } else if (variantType == QVariant::String && propertyType == qMetaTypeId<QStringList>()) {
+ } else if (variantType == QMetaType::QString && propertyType == qMetaTypeId<QStringList>()) {
QStringList list;
list << value.toString();
v = QVariant::fromValue<QStringList>(list);
@@ -1474,7 +1474,7 @@ QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engi
*/
bool QQmlProperty::write(const QVariant &value) const
{
- return QQmlPropertyPrivate::write(*this, value, nullptr);
+ return QQmlPropertyPrivate::write(*this, value, {});
}
/*!
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 285c34d7fa..8abd83d7b4 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -107,9 +107,9 @@ public:
static bool writeValueProperty(QObject *,
const QQmlPropertyData &, const QQmlPropertyData &valueTypeData,
const QVariant &, QQmlContextData *,
- QQmlPropertyData::WriteFlags flags = nullptr);
+ QQmlPropertyData::WriteFlags flags = {});
static bool write(QObject *, const QQmlPropertyData &, const QVariant &,
- QQmlContextData *, QQmlPropertyData::WriteFlags flags = nullptr);
+ QQmlContextData *, QQmlPropertyData::WriteFlags flags = {});
static void findAliasTarget(QObject *, QQmlPropertyIndex, QObject **, QQmlPropertyIndex *);
enum BindingFlag {
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 69957ab282..0e5534fc04 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -71,10 +71,11 @@ static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p)
{
QQmlPropertyData::Flags flags;
- flags.isConstant = p.isConstant();
- flags.isWritable = p.isWritable();
- flags.isResettable = p.isResettable();
- flags.isFinal = p.isFinal();
+ flags.setIsConstant(p.isConstant());
+ flags.setIsWritable(p.isWritable());
+ flags.setIsResettable(p.isResettable());
+ flags.setIsFinal(p.isFinal());
+ flags.setIsRequired(p.isRequired());
if (p.isEnumType())
flags.type = QQmlPropertyData::Flags::EnumType;
@@ -92,7 +93,7 @@ static void flagsForPropertyType(int propType, QQmlPropertyData::Flags &flags)
flags.type = QQmlPropertyData::Flags::QObjectDerivedType;
} else if (propType == QMetaType::QVariant) {
flags.type = QQmlPropertyData::Flags::QVariantType;
- } else if (propType < static_cast<int>(QVariant::UserType)) {
+ } else if (propType < static_cast<int>(QMetaType::User)) {
// nothing to do
} else if (propType == qMetaTypeId<QQmlBinding *>()) {
flags.type = QQmlPropertyData::Flags::QmlBindingType;
@@ -136,14 +137,14 @@ static void populate(QQmlPropertyData *data, const QMetaProperty &p)
void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
{
populate(this, p);
- int type = static_cast<int>(p.type());
+ int type = static_cast<int>(p.userType());
if (type == QMetaType::QObjectStar) {
setPropType(type);
m_flags.type = Flags::QObjectDerivedType;
} else if (type == QMetaType::QVariant) {
setPropType(type);
m_flags.type = Flags::QVariantType;
- } else if (type == QVariant::UserType || type == -1) {
+ } else if (type >= QMetaType::User || type == 0) {
m_flags.notFullyResolved = true;
} else {
setPropType(type);
@@ -166,21 +167,21 @@ void QQmlPropertyData::load(const QMetaMethod &m)
m_flags.type = Flags::FunctionType;
if (m.methodType() == QMetaMethod::Signal) {
- m_flags.isSignal = true;
+ m_flags.setIsSignal(true);
} else if (m.methodType() == QMetaMethod::Constructor) {
- m_flags.isConstructor = true;
+ m_flags.setIsConstructor(true);
setPropType(QMetaType::QObjectStar);
}
const int paramCount = m.parameterCount();
if (paramCount) {
- m_flags.hasArguments = true;
+ m_flags.setHasArguments(true);
if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*"))
- m_flags.isV4Function = true;
+ m_flags.setIsV4Function(true);
}
if (m.attributes() & QMetaMethod::Cloned)
- m_flags.isCloned = true;
+ m_flags.setIsCloned(true);
Q_ASSERT(m.revision() <= Q_INT16_MAX);
setRevision(m.revision());
@@ -314,13 +315,13 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
const QList<QByteArray> &names)
{
QQmlPropertyData data;
- data.setPropType(QVariant::Invalid);
+ data.setPropType(QMetaType::UnknownType);
data.setCoreIndex(coreIndex);
data.setFlags(flags);
data.setArguments(nullptr);
QQmlPropertyData handler = data;
- handler.m_flags.isSignalHandler = true;
+ handler.m_flags.setIsSignalHandler(true);
if (types) {
int argumentCount = *types;
@@ -398,6 +399,19 @@ const QMetaObject *QQmlPropertyCache::createMetaObject()
return _metaObject;
}
+QQmlPropertyData *QQmlPropertyCache::maybeUnresolvedProperty(int index) const
+{
+ if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
+ return nullptr;
+
+ QQmlPropertyData *rv = nullptr;
+ if (index < propertyIndexCacheStart)
+ return _parent->maybeUnresolvedProperty(index);
+ else
+ rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
+ return rv;
+}
+
QQmlPropertyData *QQmlPropertyCache::defaultProperty() const
{
return property(defaultPropertyName(), nullptr, nullptr);
@@ -519,7 +533,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
data->setFlags(methodFlags);
data->lazyLoad(m);
- data->m_flags.isDirect = !dynamicMetaObject;
+ data->m_flags.setIsDirect(!dynamicMetaObject);
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
@@ -527,7 +541,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (data->isSignal()) {
sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
*sigdata = *data;
- sigdata->m_flags.isSignalHandler = true;
+ sigdata->m_flags.setIsSignalHandler(true);
}
QQmlPropertyData *old = nullptr;
@@ -569,7 +583,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (old) {
// We only overload methods in the same class, exactly like C++
if (old->isFunction() && old->coreIndex() >= methodOffset)
- data->m_flags.isOverload = true;
+ data->m_flags.setIsOverload(true);
data->markAsOverrideOf(old);
}
@@ -600,7 +614,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
data->lazyLoad(p);
data->setTypeMinorVersion(typeMinorVersion);
- data->m_flags.isDirect = !dynamicMetaObject;
+ data->m_flags.setIsDirect(!dynamicMetaObject);
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
@@ -626,7 +640,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
- data->m_flags.isDirect = false;
+ data->m_flags.setIsDirect(false);
else
data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
if (old)
@@ -849,7 +863,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
setOverrideIndexIsProperty(!predecessor->isFunction());
setOverrideIndex(predecessor->coreIndex());
- predecessor->m_flags.isOverridden = true;
+ predecessor->m_flags.setIsOverridden(true);
}
QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names)
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index bfd78eef88..a5340cec37 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -118,6 +118,7 @@ public:
}
QQmlPropertyData *property(int) const;
+ QQmlPropertyData *maybeUnresolvedProperty(int) const;
QQmlPropertyData *method(int) const;
QQmlPropertyData *signal(int index) const;
QQmlEnumData *qmlEnum(int) const;
diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index 034ebfc743..36581bda4e 100644
--- a/src/qml/qml/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -74,6 +74,22 @@ int QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::Bui
return QMetaType::UnknownType;
}
+QByteArray QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(const QUrl &url)
+{
+ const QString path = url.path();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ // Not a reusable type if we don't have an absolute Url
+ if (lastSlash <= -1)
+ return QByteArray();
+ // ### this might not be correct for .ui.qml files
+ const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
+ // Not a reusable type if it doesn't start with a upper case letter.
+ if (nameBase.isEmpty() || !nameBase.at(0).isUpper())
+ return QByteArray();
+ return nameBase.toUtf8() + "_QMLTYPE_" +
+ QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+}
+
QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache)
: referencingObjectIndex(referencingObjectIndex)
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 39778aa328..6b02d6fb98 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -55,16 +55,21 @@
#include <private/qqmlmetaobject_p.h>
#include <private/qqmlpropertyresolver_p.h>
#include <private/qqmltypedata_p.h>
+#include <private/inlinecomponentutils_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
+
+#include <QScopedValueRollback>
+#include <vector>
QT_BEGIN_NAMESPACE
-inline QQmlJS::DiagnosticMessage qQmlCompileError(const QV4::CompiledData::Location &location,
+inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location,
const QString &description)
{
- QQmlJS::DiagnosticMessage error;
- error.line = location.line;
- error.column = location.column;
- error.message = description;
+ QQmlError error;
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
+ error.setDescription(description);
return error;
}
@@ -97,6 +102,8 @@ public:
static QAtomicInt classIndexCounter;
static int metaTypeForPropertyType(QV4::CompiledData::BuiltinType type);
+
+ static QByteArray createClassNameTypeByUrl(const QUrl &url);
};
template <typename ObjectContainer>
@@ -108,14 +115,19 @@ public:
QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
QQmlEnginePrivate *enginePrivate,
- const ObjectContainer *objectContainer, const QQmlImports *imports);
+ const ObjectContainer *objectContainer, const QQmlImports *imports,
+ const QByteArray &typeClassName);
- QQmlJS::DiagnosticMessage buildMetaObjects();
+ QQmlError buildMetaObjects();
+ enum class VMEMetaObjectIsRequired {
+ Maybe,
+ Always
+ };
protected:
- QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
- QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const;
- QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
+ QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const;
+ QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
int metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName = nullptr);
@@ -126,31 +138,83 @@ protected:
const QQmlImports * const imports;
QQmlPropertyCacheVector *propertyCaches;
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
+ QByteArray typeClassName; // not const as we temporarily chang it for inline components
+ unsigned int currentRoot; // set to objectID of inline component root when handling inline components
};
template <typename ObjectContainer>
inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
QQmlEnginePrivate *enginePrivate,
- const ObjectContainer *objectContainer, const QQmlImports *imports)
+ const ObjectContainer *objectContainer, const QQmlImports *imports,
+ const QByteArray &typeClassName)
: enginePrivate(enginePrivate)
, objectContainer(objectContainer)
, imports(imports)
, propertyCaches(propertyCaches)
, pendingGroupPropertyBindings(pendingGroupPropertyBindings)
+ , typeClassName(typeClassName)
+ , currentRoot(-1)
{
propertyCaches->resize(objectContainer->objectCount());
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
+inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
{
+ using namespace icutils;
QQmlBindingInstantiationContext context;
- return buildMetaObjectRecursively(/*root object*/0, context);
+
+ // get a list of all inline components
+ using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type;
+ std::vector<InlineComponent> allICs {};
+ for (int i=0; i != objectContainer->objectCount(); ++i) {
+ const CompiledObject *obj = objectContainer->objectAt(i);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ allICs.push_back(*it);
+ }
+ }
+
+ // create a graph on inline components referencing inline components
+ std::vector<Node> nodes;
+ nodes.resize(allICs.size());
+ std::iota(nodes.begin(), nodes.end(), 0);
+ AdjacencyList adjacencyList;
+ adjacencyList.resize(nodes.size());
+ fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs);
+
+ bool hasCycle = false;
+ auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
+
+ if (hasCycle) {
+ QQmlError diag;
+ diag.setDescription(QLatin1String("Inline components form a cycle!"));
+ return diag;
+ }
+
+ // create meta objects for inline components before compiling actual root component
+ for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
+ const auto &ic = allICs[nodeIt->index];
+ QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
+ Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr);
+ Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet
+
+ QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() };
+ QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName};
+ QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex};
+ QQmlError diag = buildMetaObjectRecursively(ic.objectIndex, context, VMEMetaObjectIsRequired::Always);
+ if (diag.isValid()) {
+ return diag;
+ }
+ typeRef->typePropertyCache = propertyCaches->at(ic.objectIndex);
+ Q_ASSERT(!typeRef->typePropertyCache.isNull());
+ }
+
+ return buildMetaObjectRecursively(/*root object*/0, context, VMEMetaObjectIsRequired::Maybe);
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
+inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
{
auto isAddressable = [](const QUrl &url) {
const QString fileName = url.fileName();
@@ -158,7 +222,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
};
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
- bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0
+ bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
|| obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
|| (((obj->flags & QV4::CompiledData::Object::IsComponent)
|| (objectIndex == 0 && isAddressable(objectContainer->url())))
@@ -179,7 +243,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- QQmlJS::DiagnosticMessage error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ QQmlError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
if (error.isValid())
return error;
}
@@ -194,7 +258,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
{
- QQmlJS::DiagnosticMessage error;
+ QQmlError error;
baseTypeCache = propertyCacheForObject(obj, context, &error);
if (error.isValid())
return error;
@@ -202,7 +266,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
if (baseTypeCache) {
if (needVMEMetaObject) {
- QQmlJS::DiagnosticMessage error = createMetaObject(objectIndex, obj, baseTypeCache);
+ QQmlError error = createMetaObject(objectIndex, obj, baseTypeCache);
if (error.isValid())
return error;
} else {
@@ -224,23 +288,24 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
if (!context.resolveInstantiatingProperty())
pendingGroupPropertyBindings->append(context);
- QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context);
+ QQmlError error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
if (error.isValid())
return error;
}
}
- QQmlJS::DiagnosticMessage noError;
+ QQmlError noError;
return noError;
}
template <typename ObjectContainer>
-inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const
{
if (context.instantiatingProperty) {
return context.instantiatingPropertyCache(enginePrivate);
} else if (obj->inheritedTypeNameIndex != 0) {
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
+ QQmlType qmltype = typeRef->type;
Q_ASSERT(typeRef);
if (typeRef->isFullyDynamicType) {
@@ -280,7 +345,7 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
+inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
{
QQmlRefPointer<QQmlPropertyCache> cache;
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
@@ -292,15 +357,8 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
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 (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) {
+ newClassName = typeClassName;
}
if (newClassName.isEmpty()) {
newClassName = QQmlMetaObject(baseTypeCache.data()).className();
@@ -429,7 +487,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
auto flags = QQmlPropertyData::defaultSignalFlags();
if (paramCount)
- flags.hasArguments = true;
+ flags.setHasArguments(true);
QString signalName = stringAt(s->nameIndex);
if (seenSignals.contains(signalName))
@@ -458,7 +516,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
auto formal = function->formalsBegin();
auto end = function->formalsEnd();
for ( ; formal != end; ++formal) {
- flags.hasArguments = true;
+ flags.setHasArguments(true);
parameterNames << stringAt(formal->nameIndex).toUtf8();
int type = metaTypeForParameter(formal->type);
if (type == QMetaType::UnknownType)
@@ -499,22 +557,43 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
Q_ASSERT(!p->isBuiltinType);
QQmlType qmltype;
- if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) {
+ bool selfReference = false;
+ if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr,
+ nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
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());
+ // inline components are not necessarily valid yet
+ Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType());
+ if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
+ CompositeMetaTypeIds typeIds;
+ if (qmltype.isInlineComponentType()) {
+ auto objectId = qmltype.inlineComponendId();
+ auto containingType = qmltype.containingType();
+ if (containingType.isValid()) {
+ auto icType = containingType.lookupInlineComponentById(objectId);
+ typeIds = {icType.typeId(), icType.qListTypeId()};
+ } else {
+ typeIds = {};
+ }
+ if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type
+ typeIds = objectContainer->typeIdsForComponent(objectId);
+ Q_ASSERT(typeIds.isValid());
+ } else if (selfReference) {
+ typeIds = objectContainer->typeIdsForComponent();
+ } else {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
- auto compilationUnit = tdata->compilationUnit();
+ auto compilationUnit = tdata->compilationUnit();
+ typeIds = compilationUnit->typeIdsForComponent();
+ }
if (p->isList) {
- propertyType = compilationUnit->listMetaTypeId;
+ propertyType = typeIds.listId;
} else {
- propertyType = compilationUnit->metaTypeId;
+ propertyType = typeIds.id;
}
} else {
if (p->isList) {
@@ -532,7 +611,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
}
if (!p->isReadOnly && !p->isList)
- propertyFlags.isWritable = true;
+ propertyFlags.setIsWritable(true);
QString propertyName = stringAt(p->nameIndex);
@@ -544,7 +623,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
effectiveSignalIndex++;
}
- QQmlJS::DiagnosticMessage noError;
+ QQmlError noError;
return noError;
}
@@ -562,12 +641,17 @@ inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const
if (customTypeName)
*customTypeName = typeName;
QQmlType qmltype;
- if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr))
+ bool selfReference = false;
+ if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr, nullptr, QQmlType::AnyRegistrationType,
+ &selfReference))
return QMetaType::UnknownType;
if (!qmltype.isComposite())
return qmltype.typeId();
+ if (selfReference)
+ return objectContainer->typeIdsForComponent().id;
+
QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
@@ -587,11 +671,11 @@ public:
void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
- QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
+ QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
private:
void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
- QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
+ QQmlError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
@@ -702,14 +786,14 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
+inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv)
{
*type = 0;
bool writable = false;
bool resettable = false;
- propertyFlags->isAlias = true;
+ propertyFlags->setIsAlias(true);
if (alias.aliasToLocalAlias) {
const QV4::CompiledData::Alias *lastAlias = &alias;
@@ -756,7 +840,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
if (typeRef->type.isValid())
*type = typeRef->type.typeId();
else
- *type = typeRef->compilationUnit->metaTypeId;
+ *type = typeRef->compilationUnit()->metaTypeId;
*minorVersion = typeRef->minorVersion;
@@ -794,12 +878,12 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
if (valueTypeIndex != -1) {
const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- *type = QVariant::Int;
+ *type = QMetaType::Int;
else
*type = valueTypeMetaObject->property(valueTypeIndex).userType();
} else {
if (targetProperty->isEnum()) {
- *type = QVariant::Int;
+ *type = QMetaType::Int;
} else {
// Copy type flags
propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
@@ -811,18 +895,18 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
}
}
- propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable;
- propertyFlags->isResettable = resettable;
- return QQmlJS::DiagnosticMessage();
+ propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable);
+ propertyFlags->setIsResettable(resettable);
+ return QQmlError();
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
{
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
if (!object.aliasCount())
- return QQmlJS::DiagnosticMessage();
+ return QQmlError();
QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
Q_ASSERT(propertyCache);
@@ -839,7 +923,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
int type = 0;
int minorVersion = 0;
QQmlPropertyData::Flags propertyFlags;
- QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv);
+ QQmlError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv);
if (error.isValid())
return error;
@@ -852,7 +936,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
type, minorVersion, effectiveSignalIndex++);
}
- return QQmlJS::DiagnosticMessage();
+ return QQmlError();
}
template <typename ObjectContainer>
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index dec696226e..7f70c34854 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -52,6 +52,7 @@
//
#include <private/qobject_p.h>
+#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
@@ -69,6 +70,7 @@ public:
typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
struct Flags {
+ friend class QQmlPropertyData;
enum Types {
OtherType = 0,
FunctionType = 1, // Is an invokable
@@ -87,30 +89,34 @@ public:
// trySetStaticMetaCallFunction for details.
// (Note: this padding is done here, because certain compilers have surprising behavior
// when an enum is declared in-between two bit fields.)
- enum { BitsLeftInFlags = 10 };
+ enum { BitsLeftInFlags = 15 };
unsigned otherBits : BitsLeftInFlags; // align to 32 bits
- // Can apply to all properties, except IsFunction
- unsigned isConstant : 1; // Has CONST flag
- unsigned isWritable : 1; // Has WRITE function
- unsigned isResettable : 1; // Has RESET function
- unsigned isAlias : 1; // Is a QML alias to another property
- unsigned isFinal : 1; // Has FINAL flag
- unsigned isOverridden : 1; // Is overridden by a extension property
- unsigned isDirect : 1; // Exists on a C++ QMetaObject
-
+ // Members of the form aORb can only be a when type is not FunctionType, and only be
+ // b when type equals FunctionType. For that reason, the semantic meaning of the bit is
+ // overloaded, and the accessor functions are used to get the correct value
+ //
+ // Moreover, isSignalHandler, isOverload and isCloned and isConstructor make only sense
+ // for functions, too (and could at a later point be reused for flags that only make sense
+ // for non-functions)
+ //
+ // Lastly, isDirect and isOverridden apply to both functions and non-functions
+ private:
+ unsigned isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML
+ unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments
+ unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal
+ unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML
+ unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args
+ unsigned isSignalHandler : 1; // Function is a signal handler
+ unsigned isOverload : 1; // Function is an overload of another function
+ unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned
+ unsigned isConstructor : 1; // The function was marked is a constructor
+ unsigned isDirect : 1; // Exists on a C++ QMetaObject
+ unsigned isOverridden : 1; // Is overridden by a extension property
+ public:
unsigned type : 4; // stores an entry of Types
// Apply only to IsFunctions
- unsigned isVMEFunction : 1; // Function was added by QML
- unsigned hasArguments : 1; // Function takes arguments
- unsigned isSignal : 1; // Function is a signal
- unsigned isVMESignal : 1; // Signal was added by QML
- unsigned isV4Function : 1; // Function takes QQmlV4Function* args
- unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
- unsigned isCloned : 1; // The function was marked as cloned
- unsigned isConstructor : 1; // The function was marked is a constructor
// Internal QQmlPropertyCache flags
unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
@@ -119,8 +125,90 @@ public:
inline Flags();
inline bool operator==(const Flags &other) const;
inline void copyPropertyTypeFlags(Flags from);
+
+ void setIsConstant(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isConstantORisVMEFunction = b;
+ }
+
+ void setIsWritable(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isWritableORhasArguments = b;
+ }
+
+ void setIsResettable(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isResettableORisSignal = b;
+ }
+
+ void setIsAlias(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isAliasORisVMESignal = b;
+ }
+
+ void setIsFinal(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isFinalORisV4Function = b;
+ }
+
+ void setIsOverridden(bool b) {
+ isOverridden = b;
+ }
+
+ void setIsDirect(bool b) {
+ isDirect = b;
+ }
+
+ void setIsRequired(bool b) {
+ Q_ASSERT(type != FunctionType);
+ isRequiredORisCloned = b;
+ }
+
+ void setIsVMEFunction(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isConstantORisVMEFunction = b;
+ }
+ void setHasArguments(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isWritableORhasArguments = b;
+ }
+ void setIsSignal(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isResettableORisSignal = b;
+ }
+ void setIsVMESignal(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isAliasORisVMESignal = b;
+ }
+
+ void setIsV4Function(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isFinalORisV4Function = b;
+ }
+
+ void setIsSignalHandler(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isSignalHandler = b;
+ }
+
+ void setIsOverload(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isOverload = b;
+ }
+
+ void setIsCloned(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isRequiredORisCloned = b;
+ }
+
+ void setIsConstructor(bool b) {
+ Q_ASSERT(type == FunctionType);
+ isConstructor = b;
+ }
+
};
+
inline bool operator==(const QQmlPropertyData &) const;
Flags flags() const { return m_flags; }
@@ -133,14 +221,15 @@ public:
bool isValid() const { return coreIndex() != -1; }
- bool isConstant() const { return m_flags.isConstant; }
- bool isWritable() const { return m_flags.isWritable; }
- void setWritable(bool onoff) { m_flags.isWritable = onoff; }
- bool isResettable() const { return m_flags.isResettable; }
- bool isAlias() const { return m_flags.isAlias; }
- bool isFinal() const { return m_flags.isFinal; }
+ bool isConstant() const { return !isFunction() && m_flags.isConstantORisVMEFunction; }
+ bool isWritable() const { return !isFunction() && m_flags.isWritableORhasArguments; }
+ void setWritable(bool onoff) { Q_ASSERT(!isFunction()); m_flags.isWritableORhasArguments = onoff; }
+ bool isResettable() const { return !isFunction() && m_flags.isResettableORisSignal; }
+ bool isAlias() const { return !isFunction() && m_flags.isAliasORisVMESignal; }
+ bool isFinal() const { return !isFunction() && m_flags.isFinalORisV4Function; }
bool isOverridden() const { return m_flags.isOverridden; }
- bool isDirect() const { return m_flags.isDirect; }
+ bool isDirect() const { return m_flags.isOverload; }
+ bool isRequired() const { return !isFunction() && m_flags.isRequiredORisCloned; }
bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
bool isFunction() const { return m_flags.type == Flags::FunctionType; }
bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; }
@@ -150,15 +239,15 @@ public:
bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; }
bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; }
bool isQVariant() const { return m_flags.type == Flags::QVariantType; }
- bool isVMEFunction() const { return m_flags.isVMEFunction; }
- bool hasArguments() const { return m_flags.hasArguments; }
- bool isSignal() const { return m_flags.isSignal; }
- bool isVMESignal() const { return m_flags.isVMESignal; }
- bool isV4Function() const { return m_flags.isV4Function; }
+ bool isVMEFunction() const { return isFunction() && m_flags.isConstantORisVMEFunction; }
+ bool hasArguments() const { return isFunction() && m_flags.isWritableORhasArguments; }
+ bool isSignal() const { return isFunction() && m_flags.isResettableORisSignal; }
+ bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; }
+ bool isV4Function() const { return isFunction() && m_flags.isFinalORisV4Function; }
bool isSignalHandler() const { return m_flags.isSignalHandler; }
bool isOverload() const { return m_flags.isOverload; }
void setOverload(bool onoff) { m_flags.isOverload = onoff; }
- bool isCloned() const { return m_flags.isCloned; }
+ bool isCloned() const { return isFunction() && m_flags.isRequiredORisCloned; }
bool isConstructor() const { return m_flags.isConstructor; }
bool hasOverride() const { return overrideIndex() >= 0; }
@@ -294,9 +383,9 @@ public:
static Flags defaultSignalFlags()
{
Flags f;
- f.isSignal = true;
f.type = Flags::FunctionType;
- f.isVMESignal = true;
+ f.setIsSignal(true);
+ f.setIsVMESignal(true);
return f;
}
@@ -304,7 +393,7 @@ public:
{
Flags f;
f.type = Flags::FunctionType;
- f.isVMEFunction = true;
+ f.setIsVMEFunction(true);
return f;
}
@@ -348,44 +437,32 @@ bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
QQmlPropertyData::Flags::Flags()
: otherBits(0)
- , isConstant(false)
- , isWritable(false)
- , isResettable(false)
- , isAlias(false)
- , isFinal(false)
- , isOverridden(false)
- , isDirect(false)
- , type(OtherType)
- , isVMEFunction(false)
- , hasArguments(false)
- , isSignal(false)
- , isVMESignal(false)
- , isV4Function(false)
+ , isConstantORisVMEFunction(false)
+ , isWritableORhasArguments(false)
+ , isResettableORisSignal(false)
+ , isAliasORisVMESignal(false)
+ , isFinalORisV4Function(false)
, isSignalHandler(false)
, isOverload(false)
- , isCloned(false)
+ , isRequiredORisCloned(false)
, isConstructor(false)
+ , isOverridden(false)
+ , type(OtherType)
, notFullyResolved(false)
, overrideIndexIsProperty(false)
{}
bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const
{
- return isConstant == other.isConstant &&
- isWritable == other.isWritable &&
- isResettable == other.isResettable &&
- isAlias == other.isAlias &&
- isFinal == other.isFinal &&
+ return isConstantORisVMEFunction == other.isConstantORisVMEFunction &&
+ isWritableORhasArguments == other.isWritableORhasArguments &&
+ isResettableORisSignal == other.isResettableORisSignal &&
+ isAliasORisVMESignal == other.isAliasORisVMESignal &&
+ isFinalORisV4Function == other.isFinalORisV4Function &&
isOverridden == other.isOverridden &&
- type == other.type &&
- isVMEFunction == other.isVMEFunction &&
- hasArguments == other.hasArguments &&
- isSignal == other.isSignal &&
- isVMESignal == other.isVMESignal &&
- isV4Function == other.isV4Function &&
isSignalHandler == other.isSignalHandler &&
- isOverload == other.isOverload &&
- isCloned == other.isCloned &&
+ isRequiredORisCloned == other.isRequiredORisCloned &&
+ type == other.type &&
isConstructor == other.isConstructor &&
notFullyResolved == other.notFullyResolved &&
overrideIndexIsProperty == other.overrideIndexIsProperty;
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index c75055a3b0..3587609301 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -73,7 +73,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, c
bindingPropertyDataPerObject->resize(compilationUnit->objectCount());
}
-QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validate()
+QVector<QQmlError> QQmlPropertyValidator::validate()
{
return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr);
}
@@ -96,12 +96,15 @@ struct BindingFinder
}
};
-QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
+QVector<QQmlError> QQmlPropertyValidator::validateObject(
int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
{
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr);
+ }
- if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ if (obj->flags & QV4::CompiledData::Object::IsComponent && !(obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)) {
Q_ASSERT(obj->nBindings == 1);
const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
@@ -110,7 +113,7 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
if (!propertyCache)
- return QVector<QQmlJS::DiagnosticMessage>();
+ return QVector<QQmlError>();
QQmlCustomParser *customParser = nullptr;
if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
@@ -220,7 +223,7 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
= pd
&& QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())
&& !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment);
- const QVector<QQmlJS::DiagnosticMessage> subObjectValidatorErrors
+ const QVector<QQmlError> subObjectValidatorErrors
= validateObject(binding->value.objectIndex, binding,
populatingValueTypeGroupProperty);
if (!subObjectValidatorErrors.isEmpty())
@@ -277,11 +280,11 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
}
if (binding->type < QV4::CompiledData::Binding::Type_Script) {
- QQmlJS::DiagnosticMessage bindingError = validateLiteralBinding(propertyCache, pd, binding);
+ QQmlError bindingError = validateLiteralBinding(propertyCache, pd, binding);
if (bindingError.isValid())
return recordError(bindingError);
} else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- QQmlJS::DiagnosticMessage bindingError = validateObjectBinding(pd, name, binding);
+ QQmlError bindingError = validateObjectBinding(pd, name, binding);
if (bindingError.isValid())
return recordError(bindingError);
} else if (binding->isGroupProperty()) {
@@ -343,24 +346,24 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
customParser->validator = nullptr;
customParser->engine = nullptr;
customParser->imports = (QQmlImports*)nullptr;
- QVector<QQmlJS::DiagnosticMessage> parserErrors = customParser->errors();
+ QVector<QQmlError> parserErrors = customParser->errors();
if (!parserErrors.isEmpty())
return parserErrors;
}
(*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData;
- QVector<QQmlJS::DiagnosticMessage> noError;
+ QVector<QQmlError> noError;
return noError;
}
-QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
+QQmlError 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"));
}
- QQmlJS::DiagnosticMessage noError;
+ QQmlError noError;
if (property->isEnum()) {
if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
@@ -384,8 +387,8 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
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.setLine(qmlConvertSourceCoordinate<quint32, int>(binding->valueLocation.line));
+ warning.setColumn(qmlConvertSourceCoordinate<quint32, int>(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."));
@@ -398,31 +401,31 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
switch (property->propType()) {
case QMetaType::QVariant:
break;
- case QVariant::String: {
+ case QMetaType::QString: {
if (!binding->evaluatesToString()) {
return warnOrError(tr("Invalid property assignment: string expected"));
}
}
break;
- case QVariant::StringList: {
+ case QMetaType::QStringList: {
if (!binding->evaluatesToString()) {
return warnOrError(tr("Invalid property assignment: string or string list expected"));
}
}
break;
- case QVariant::ByteArray: {
+ case QMetaType::QByteArray: {
if (binding->type != QV4::CompiledData::Binding::Type_String) {
return warnOrError(tr("Invalid property assignment: byte array expected"));
}
}
break;
- case QVariant::Url: {
+ case QMetaType::QUrl: {
if (binding->type != QV4::CompiledData::Binding::Type_String) {
return warnOrError(tr("Invalid property assignment: url expected"));
}
}
break;
- case QVariant::UInt: {
+ case QMetaType::UInt: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
double d = compilationUnit->bindingValueAsNumber(binding);
if (double(uint(d)) == d)
@@ -431,7 +434,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
return warnOrError(tr("Invalid property assignment: unsigned int expected"));
}
break;
- case QVariant::Int: {
+ case QMetaType::Int: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
double d = compilationUnit->bindingValueAsNumber(binding);
if (double(int(d)) == d)
@@ -446,13 +449,13 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Double: {
+ case QMetaType::Double: {
if (binding->type != QV4::CompiledData::Binding::Type_Number) {
return warnOrError(tr("Invalid property assignment: number expected"));
}
}
break;
- case QVariant::Color: {
+ case QMetaType::QColor: {
bool ok = false;
QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -461,7 +464,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
break;
#if QT_CONFIG(datestring)
- case QVariant::Date: {
+ case QMetaType::QDate: {
bool ok = false;
QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -469,7 +472,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Time: {
+ case QMetaType::QTime: {
bool ok = false;
QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -477,7 +480,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::DateTime: {
+ case QMetaType::QDateTime: {
bool ok = false;
QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -486,7 +489,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
break;
#endif // datestring
- case QVariant::Point: {
+ case QMetaType::QPoint: {
bool ok = false;
QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -494,7 +497,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::PointF: {
+ case QMetaType::QPointF: {
bool ok = false;
QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -502,7 +505,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Size: {
+ case QMetaType::QSize: {
bool ok = false;
QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -510,7 +513,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::SizeF: {
+ case QMetaType::QSizeF: {
bool ok = false;
QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -518,7 +521,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Rect: {
+ case QMetaType::QRect: {
bool ok = false;
QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -526,7 +529,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::RectF: {
+ case QMetaType::QRectF: {
bool ok = false;
QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
@@ -534,13 +537,13 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Bool: {
+ case QMetaType::Bool: {
if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
return warnOrError(tr("Invalid property assignment: boolean expected"));
}
}
break;
- case QVariant::Vector2D: {
+ case QMetaType::QVector2D: {
struct {
float xp;
float yp;
@@ -550,7 +553,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Vector3D: {
+ case QMetaType::QVector3D: {
struct {
float xp;
float yp;
@@ -561,7 +564,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Vector4D: {
+ case QMetaType::QVector4D: {
struct {
float xp;
float yp;
@@ -573,7 +576,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::Quaternion: {
+ case QMetaType::QQuaternion: {
struct {
float wp;
float xp;
@@ -585,8 +588,8 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp
}
}
break;
- case QVariant::RegExp:
- case QVariant::RegularExpression:
+ case QMetaType::QRegExp:
+ case QMetaType::QRegularExpression:
return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
default: {
// generate single literal value assignment to a list property if required
@@ -656,23 +659,23 @@ bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
return false;
}
-QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
+QVector<QQmlError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
{
- QVector<QQmlJS::DiagnosticMessage> errors;
+ QVector<QQmlError> errors;
errors.append(qQmlCompileError(location, description));
return errors;
}
-QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QQmlJS::DiagnosticMessage &error) const
+QVector<QQmlError> QQmlPropertyValidator::recordError(const QQmlError &error) const
{
- QVector<QQmlJS::DiagnosticMessage> errors;
+ QVector<QQmlError> errors;
errors.append(error);
return errors;
}
-QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
+QQmlError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
{
- QQmlJS::DiagnosticMessage noError;
+ QQmlError noError;
if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
@@ -727,11 +730,11 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
} else if (isPrimitiveType(propType)) {
- auto typeName = QMetaType::typeName(propType);
+ auto typeName = QString::fromUtf8(QMetaType::typeName(propType));
return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting \"%3\"")
.arg(rhsType())
.arg(propertyName)
- .arg(QString::fromUtf8(typeName)));
+ .arg(typeName));
} else if (QQmlValueTypeFactory::isValueType(propType)) {
return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting an object")
.arg(rhsType()).arg(propertyName));
diff --git a/src/qml/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h
index 74a1281927..281472981c 100644
--- a/src/qml/qml/qqmlpropertyvalidator_p.h
+++ b/src/qml/qml/qqmlpropertyvalidator_p.h
@@ -66,25 +66,24 @@ class QQmlPropertyValidator
public:
QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
- QVector<QQmlJS::DiagnosticMessage> validate();
+ QVector<QQmlError> validate();
private:
- QVector<QQmlJS::DiagnosticMessage> validateObject(
+ QVector<QQmlError> validateObject(
int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
bool populatingValueTypeGroupProperty = false) const;
- QQmlJS::DiagnosticMessage validateLiteralBinding(
+ QQmlError validateLiteralBinding(
QQmlPropertyCache *propertyCache, QQmlPropertyData *property,
const QV4::CompiledData::Binding *binding) const;
- QQmlJS::DiagnosticMessage validateObjectBinding(
+ QQmlError validateObjectBinding(
QQmlPropertyData *property, const QString &propertyName,
const QV4::CompiledData::Binding *binding) const;
bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
- Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError(
+ Q_REQUIRED_RESULT QVector<QQmlError> recordError(
const QV4::CompiledData::Location &location, const QString &description) const;
- Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError(
- const QQmlJS::DiagnosticMessage &error) const;
+ Q_REQUIRED_RESULT QVector<QQmlError> recordError(const QQmlError &error) const;
QString stringAt(int index) const { return compilationUnit->stringAt(index); }
QV4::ResolvedTypeReference *resolvedType(int id) const
{
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index eb103dc434..dac21f7ee7 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -41,6 +41,7 @@
#include <private/qqmlirbuilder_p.h>
#include <private/qqmlscriptblob_p.h>
#include <private/qqmlscriptdata_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <private/qv4runtimecodegen_p.h>
#include <private/qv4script_p.h>
@@ -167,8 +168,8 @@ void QQmlScriptBlob::done()
QList<QQmlError> errors = script.script->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(script.location.line);
- error.setColumn(script.location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column));
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
errors.prepend(error);
setError(errors);
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index 97fcba5009..317d3c7ef5 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -84,6 +84,9 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
case QQmlType::CompositeType:
extraData.fd = new QQmlCompositeTypeData;
break;
+ case QQmlType::InlineComponentType:
+ extraData.id = new QQmlInlineTypeData;
+ break;
default: qFatal("QQmlTypePrivate Internal Error.");
}
}
@@ -106,6 +109,9 @@ QQmlTypePrivate::~QQmlTypePrivate()
case QQmlType::CompositeType:
delete extraData.fd;
break;
+ case QQmlType::InlineComponentType:
+ delete extraData.id;
+ break;
default: //Also InterfaceType, because it has no extra data
break;
}
@@ -426,6 +432,12 @@ void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cach
insertEnums(cppMetaObject);
}
+void QQmlTypePrivate::setContainingType(QQmlType *containingType)
+{
+ Q_ASSERT(regType == QQmlType::InlineComponentType);
+ extraData.id->containingType = containingType->d.data();
+}
+
void QQmlTypePrivate::setName(const QString &uri, const QString &element)
{
module = uri;
@@ -440,6 +452,8 @@ QByteArray QQmlType::typeName() const
return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
else if (d->baseMetaObject)
return d->baseMetaObject->className();
+ else if (d->regType == InlineComponentType)
+ return d->extraData.id->inlineComponentName.toUtf8();
}
return QByteArray();
}
@@ -561,7 +575,11 @@ bool QQmlType::isComposite() const
bool QQmlType::isCompositeSingleton() const
{
- return d && d->regType == CompositeSingletonType;
+ // if the outer type is a composite singleton, d->regType will indicate that even for
+ // the inline component type
+ // however, inline components can -at least for now- never be singletons
+ // so we just do one additional check
+ return d && d->regType == CompositeSingletonType && !isInlineComponentType();
}
bool QQmlType::isQObjectSingleton() const
@@ -618,14 +636,14 @@ int QQmlType::metaObjectRevision() const
QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const
{
- if (const QQmlTypePrivate *base = d->attachedPropertiesBase(engine))
+ if (const QQmlTypePrivate *base = d ? d->attachedPropertiesBase(engine) : nullptr)
return base->extraData.cd->attachedPropertiesFunc;
return nullptr;
}
const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const
{
- if (const QQmlTypePrivate *base = d->attachedPropertiesBase(engine))
+ if (const QQmlTypePrivate *base = d ? d->attachedPropertiesBase(engine) : nullptr)
return base->extraData.cd->attachedPropertiesType;
return nullptr;
}
@@ -677,9 +695,28 @@ int QQmlType::index() const
return d ? d->index : -1;
}
+bool QQmlType::isInlineComponentType() const {
+ return d ? d->regType == QQmlType::InlineComponentType : false;
+}
+
+int QQmlType::inlineComponendId() const {
+ bool ok = false;
+ if (d->regType == QQmlType::RegistrationType::InlineComponentType) {
+ Q_ASSERT(d->extraData.id->objectId != -1);
+ return d->extraData.id->objectId;
+ }
+ int subObjectId = sourceUrl().fragment().toInt(&ok);
+ return ok ? subObjectId : -1;
+}
+
QUrl QQmlType::sourceUrl() const
{
- return d ? d->sourceUrl() : QUrl();
+ auto url = d ? d->sourceUrl() : QUrl();
+ if (url.isValid() && d->regType == QQmlType::RegistrationType::InlineComponentType && d->extraData.id->objectId) {
+ Q_ASSERT(url.hasFragment());
+ url.setFragment(QString::number(inlineComponendId()));
+ }
+ return url;
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
@@ -845,6 +882,19 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scope
return -1;
}
+int QQmlType::inlineComponentObjectId()
+{
+ if (!isInlineComponentType())
+ return -1;
+ return d->extraData.id->objectId;
+}
+
+void QQmlType::setInlineComponentObjectId(int id) const
+{
+ Q_ASSERT(d && d->regType == QQmlType::InlineComponentType);
+ d->extraData.id->objectId = id;
+}
+
void QQmlType::refHandle(const QQmlTypePrivate *priv)
{
if (priv)
@@ -864,4 +914,66 @@ int QQmlType::refCount(const QQmlTypePrivate *priv)
return -1;
}
+int QQmlType::lookupInlineComponentIdByName(const QString &name) const
+{
+ Q_ASSERT(d);
+ return d->namesToInlineComponentObjectIndex.value(name, -1);
+}
+
+QQmlType QQmlType::containingType() const
+{
+ Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
+ auto ret = QQmlType {d->extraData.id->containingType};
+ Q_ASSERT(!ret.isInlineComponentType());
+ return ret;
+}
+
+QQmlType QQmlType::lookupInlineComponentById(int objectid) const
+{
+ Q_ASSERT(d);
+ return d->objectIdToICType.value(objectid, QQmlType(nullptr));
+}
+
+int QQmlType::generatePlaceHolderICId() const
+{
+ Q_ASSERT(d);
+ int id = -2;
+ for (auto it = d->objectIdToICType.keyBegin(); it != d->objectIdToICType.keyEnd(); ++it)
+ if (*it < id)
+ id = *it;
+ return id;
+}
+
+void QQmlType::associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType)
+{
+ bool const reuseExistingType = existingType.isValid();
+ auto priv = reuseExistingType ? const_cast<QQmlTypePrivate *>(existingType.d.data()) : new QQmlTypePrivate { RegistrationType::InlineComponentType } ;
+ priv->setName( QString::fromUtf8(typeName()), name);
+ auto icUrl = QUrl(sourceUrl());
+ icUrl.setFragment(QString::number(objectID));
+ priv->extraData.id->url = icUrl;
+ priv->extraData.id->containingType = d.data();
+ priv->extraData.id->objectId = objectID;
+ priv->typeId = metaTypeIds.id;
+ priv->listId = metaTypeIds.listId;
+ d->namesToInlineComponentObjectIndex.insert(name, objectID);
+ QQmlType icType(priv);
+ d->objectIdToICType.insert(objectID, icType);
+ if (!reuseExistingType)
+ priv->release();
+}
+
+void QQmlType::setPendingResolutionName(const QString &name)
+{
+ Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
+ Q_ASSERT(d->extraData.id->inlineComponentName == name|| d->extraData.id->inlineComponentName.isEmpty());
+ d->extraData.id->inlineComponentName = name;
+}
+
+QString QQmlType::pendingResolutionName() const
+{
+ Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
+ return d->extraData.id->inlineComponentName;
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index af134b21f1..387baa74bb 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -74,6 +74,7 @@ class QQmlPropertyCache;
namespace QV4 {
struct String;
}
+struct CompositeMetaTypeIds;
class Q_QML_PRIVATE_EXPORT QQmlType
{
@@ -144,6 +145,9 @@ public:
int index() const;
+ bool isInlineComponentType() const;
+ int inlineComponendId() const;
+
struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo
{
QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr;
@@ -166,6 +170,8 @@ public:
int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const;
+ int inlineComponentObjectId();
+ void setInlineComponentObjectId(int id) const; // TODO: const setters are BAD
const QQmlTypePrivate *priv() const { return d.data(); }
static void refHandle(const QQmlTypePrivate *priv);
@@ -178,9 +184,19 @@ public:
InterfaceType = 2,
CompositeType = 3,
CompositeSingletonType = 4,
+ InlineComponentType = 5,
AnyRegistrationType = 255
};
+ QQmlType containingType() const;
+ int lookupInlineComponentIdByName(const QString &name) const;
+ QQmlType lookupInlineComponentById(int objectid) const;
+ int generatePlaceHolderICId() const;
+
+ void associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType);
+ void setPendingResolutionName(const QString &name);
+ QString pendingResolutionName() const;
+
private:
friend class QQmlTypePrivate;
friend uint qHash(const QQmlType &t, uint seed);
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index 51f776178c..43344827db 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -56,6 +56,7 @@
#include <private/qqmlproxymetaobject_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlmetatype_p.h>
QT_BEGIN_NAMESPACE
@@ -69,6 +70,7 @@ public:
void initEnums(QQmlEnginePrivate *engine) const;
void insertEnums(const QMetaObject *metaObject) const;
void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const;
+ void setContainingType(QQmlType *containingType);
QUrl sourceUrl() const
{
@@ -77,6 +79,8 @@ public:
return extraData.fd->url;
case QQmlType::CompositeSingletonType:
return extraData.sd->singletonInstanceInfo->url;
+ case QQmlType::InlineComponentType:
+ return extraData.id->url;
default:
return QUrl();
}
@@ -130,10 +134,23 @@ public:
QUrl url;
};
+ struct QQmlInlineTypeData
+ {
+ QUrl url = QUrl();
+ // The containing type stores a pointer to the inline component type
+ // Using QQmlType here would create a reference cycle
+ // As the inline component type cannot outlive the containing type
+ // this should still be fine
+ QQmlTypePrivate const * containingType = nullptr;
+ QString inlineComponentName = QString();
+ int objectId = -1;
+ };
+
union extraData {
QQmlCppTypeData* cd;
QQmlSingletonTypeData* sd;
QQmlCompositeTypeData* fd;
+ QQmlInlineTypeData* id;
} extraData;
const char *iid;
@@ -160,6 +177,8 @@ public:
mutable QList<QStringHash<int>*> scopedEnums;
void setName(const QString &uri, const QString &element);
+ mutable QHash<QString, int> namesToInlineComponentObjectIndex;
+ mutable QHash<int, QQmlType> objectIdToICType;
private:
~QQmlTypePrivate() override;
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 9ff0e3fb9e..058bf8848c 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -58,10 +58,10 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
- , typeData(typeData)
, dependencyHasher(dependencyHasher)
- , typeNameCache(typeNameCache)
, document(parsedQML)
+ , typeNameCache(typeNameCache)
+ , typeData(typeData)
{
}
@@ -81,8 +81,8 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
{
QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings,
- engine, this, imports());
- QQmlJS::DiagnosticMessage error = propertyCacheBuilder.buildMetaObjects();
+ engine, this, imports(), typeData->typeClassName());
+ QQmlError error = propertyCacheBuilder.buildMetaObjects();
if (error.isValid()) {
recordError(error);
return nullptr;
@@ -174,8 +174,8 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
{
QQmlError error;
- error.setLine(location.line);
- error.setColumn(location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column));
error.setDescription(description);
error.setUrl(url());
errors << error;
@@ -185,8 +185,15 @@ void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message)
{
QQmlError error;
error.setDescription(message.message);
- error.setLine(message.line);
- error.setColumn(message.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(message.loc.startLine));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(message.loc.startColumn));
+ error.setUrl(url());
+ errors << error;
+}
+
+void QQmlTypeCompiler::recordError(const QQmlError &e)
+{
+ QQmlError error = e;
error.setUrl(url());
errors << error;
}
@@ -279,6 +286,11 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier
document->imports.append(import);
}
+CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const
+{
+ return typeData->typeIds(objectId);
+}
+
QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
: compiler(typeCompiler)
{
@@ -801,8 +813,8 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
const QMetaObject *firstMetaObject = nullptr;
if (tr->type.isValid())
firstMetaObject = tr->type.metaObject();
- else if (tr->compilationUnit)
- firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ else if (const auto compilationUnit = tr->compilationUnit())
+ firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
if (isUsableComponent(firstMetaObject))
continue;
// if here, not a QQmlComponent, so needs wrapping
@@ -876,6 +888,10 @@ bool QQmlComponentAndAliasResolver::resolve()
const int objCountWithoutSynthesizedComponents = qmlObjects->count();
for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) {
QmlIR::Object *obj = qmlObjects->at(i);
+ if (obj->isInlineComponent) {
+ componentRoots.append(i);
+ continue;
+ }
QQmlPropertyCache *cache = propertyCaches.at(i);
if (obj->inheritedTypeNameIndex == 0 && !cache)
continue;
@@ -931,7 +947,7 @@ bool QQmlComponentAndAliasResolver::resolve()
_objectsWithAliases.clear();
- if (!collectIdsAndAliases(rootBinding->value.objectIndex))
+ if (!collectIdsAndAliases(component->isInlineComponent ? componentRoots.at(i) : rootBinding->value.objectIndex))
return false;
component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
@@ -1007,7 +1023,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
for (int objectIndex: qAsConst(_objectsWithAliases)) {
- QQmlJS::DiagnosticMessage error;
+ QQmlError error;
const auto result = resolveAliasesInObject(objectIndex, &error);
if (error.isValid()) {
@@ -1016,7 +1032,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
}
if (result == AllAliasesResolved) {
- QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
+ QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
if (error.isValid()) {
recordError(error);
return false;
@@ -1047,7 +1063,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
QQmlComponentAndAliasResolver::AliasResolutionResult
QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
- QQmlJS::DiagnosticMessage *error)
+ QQmlError *error)
{
const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
if (!obj->aliasCount())
@@ -1216,7 +1232,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
if (obj->idNameIndex != 0)
_seenObjectWithId = true;
- if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index 40b0337848..057dd012c6 100644
--- a/src/qml/qml/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -79,7 +79,9 @@ struct QQmlTypeCompiler
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
public:
- QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document,
+ QQmlTypeCompiler(QQmlEnginePrivate *engine,
+ QQmlTypeData *typeData,
+ QmlIR::Document *document,
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
@@ -98,7 +100,8 @@ public:
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(const QV4::CompiledData::Location &location, const QString &description);
- void recordError(const QQmlJS::DiagnosticMessage &error);
+ void recordError(const QQmlJS::DiagnosticMessage &message);
+ void recordError(const QQmlError &e);
int registerString(const QString &str);
int registerConstant(QV4::ReturnedValue v);
@@ -129,12 +132,12 @@ public:
return resolvedTypes->value(id);
}
+ CompositeMetaTypeIds typeIdsForComponent(int objectId = 0) const;
+
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;
@@ -142,6 +145,9 @@ private:
// index in first hash is component index, vector inside contains object indices of objects with id property
QVector<quint32> m_componentRoots;
QQmlPropertyCacheVector m_propertyCaches;
+
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ QQmlTypeData *typeData;
};
struct QQmlCompilePass
@@ -152,7 +158,7 @@ struct QQmlCompilePass
protected:
void recordError(const QV4::CompiledData::Location &location, const QString &description) const
{ compiler->recordError(location, description); }
- void recordError(const QQmlJS::DiagnosticMessage &error)
+ void recordError(const QQmlError &error)
{ compiler->recordError(error); }
QV4::ResolvedTypeReference *resolvedType(int id) const
@@ -275,7 +281,7 @@ protected:
AllAliasesResolved
};
- AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlJS::DiagnosticMessage *error);
+ AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
QQmlEnginePrivate *enginePrivate;
QQmlJS::MemoryPool *pool;
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 0fd5bc83e6..fc1d0cfbcf 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -92,6 +92,25 @@ QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const
return m_compiledData.data();
}
+QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnitForInlineComponent(unsigned int icObjectId) const
+{
+ Q_ASSERT(m_document || m_compiledData);
+ if (m_compiledData)
+ return m_compiledData.data();
+ for (auto it = m_document->objects.begin(); it != m_document->objects.end(); ++it) {
+ auto object = *it;
+ auto icIt = std::find_if(object->inlineComponentsBegin(), object->inlineComponentsEnd(), [&](const QV4::CompiledData::InlineComponent &ic) {
+ return ic.objectIndex == icObjectId;
+ });
+ if (icIt != object->inlineComponentsEnd()) {
+ Q_ASSERT(m_inlineComponentToCompiledData.contains(icIt->nameIndex));
+ return m_inlineComponentToCompiledData[icIt->nameIndex].data();
+ }
+ }
+ Q_UNREACHABLE();
+ return nullptr; // make integrity happy
+}
+
void QQmlTypeData::registerCallback(TypeDataCallback *callback)
{
Q_ASSERT(!m_callbacks.contains(callback));
@@ -105,6 +124,13 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
+CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const
+{
+ if (objectId != 0)
+ return m_inlineComponentData[objectId].typeIds;
+ return m_typeIds;
+}
+
bool QQmlTypeData::tryLoadFromDiskCache()
{
if (!diskCacheEnabled())
@@ -130,8 +156,15 @@ bool QQmlTypeData::tryLoadFromDiskCache()
m_compiledData = unit;
- for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i)
- m_typeReferences.collectFromObject(m_compiledData->objectAt(i));
+ QVector<QV4::CompiledData::InlineComponent> ics;
+ for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) {
+ auto object = m_compiledData->objectAt(i);
+ m_typeReferences.collectFromObject(object);
+ const auto inlineComponentTable = object->inlineComponentTable();
+ for (auto i = 0; i != object->nInlineComponents; ++i) {
+ ics.push_back(inlineComponentTable[i]);
+ }
+ }
m_importCache.setBaseUrl(finalUrl(), finalUrlString());
@@ -170,14 +203,28 @@ bool QQmlTypeData::tryLoadFromDiskCache()
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return false;
}
}
+ QQmlType containingType;
+ auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first();
+ int major = -1, minor = -1;
+ QQmlImportNamespace *ns = nullptr;
+ m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns);
+ for (auto&& ic: ics) {
+ QString const nameString = m_compiledData->stringAt(ic.nameIndex);
+ QByteArray const name = nameString.toUtf8();
+ auto importUrl = finalUrl();
+ importUrl.setFragment(QString::number(ic.objectIndex));
+ auto import = new QQmlImportInstance();
+ m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType);
+ }
+
return true;
}
@@ -196,8 +243,8 @@ void QQmlTypeData::createTypeAndPropertyCaches(
{
QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator(
&m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
- m_compiledData.data(), &m_importCache);
- QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects();
+ m_compiledData.data(), &m_importCache, typeClassName());
+ QQmlError error = propertyCacheCreator.buildMetaObjects();
if (error.isValid()) {
setError(error);
return;
@@ -228,6 +275,43 @@ static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeRefere
return true;
}
+// local helper function for inline components
+namespace {
+template<typename ObjectContainer>
+void setupICs(const ObjectContainer &container, QHash<int, InlineComponentData> *icData, const QUrl &finalUrl) {
+ Q_ASSERT(icData->empty());
+ for (int i = 0; i != container->objectCount(); ++i) {
+ auto root = container->objectAt(i);
+ for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
+ auto url = finalUrl;
+ url.setFragment(QString::number(it->objectIndex));
+ const QByteArray &className = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url);
+ InlineComponentData icDatum(QQmlMetaType::registerInternalCompositeType(className), int(it->objectIndex), int(it->nameIndex), 0, 0, 0);
+ icData->insert(it->objectIndex, icDatum);
+ }
+ }
+};
+}
+
+template<typename Container>
+void QQmlTypeData::setCompileUnit(const Container &container)
+{
+ for (int i = 0; i != container->objectCount(); ++i) {
+ auto const root = container->objectAt(i);
+ for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
+ auto *typeRef = m_compiledData->resolvedType(it->nameIndex);
+
+ // We don't want the type reference to keep a strong reference to the compilation unit
+ // here. The compilation unit owns the type reference, and having a strong reference
+ // would prevent the compilation unit from ever getting deleted. We can still be sure
+ // that the compilation unit outlives the type reference, due to ownership.
+ typeRef->setReferencesCompilationUnit(false);
+
+ typeRef->setCompilationUnit(m_compiledData); // share compilation unit
+ }
+ }
+}
+
void QQmlTypeData::done()
{
auto cleanup = qScopeGuard([this]{
@@ -248,8 +332,8 @@ void QQmlTypeData::done()
QList<QQmlError> errors = script.script->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(script.location.line);
- error.setColumn(script.location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column));
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
errors.prepend(error);
setError(errors);
@@ -258,18 +342,38 @@ void QQmlTypeData::done()
}
// Check all type dependencies for errors
- for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end;
+ for (auto it = qAsConst(m_resolvedTypes).begin(), end = qAsConst(m_resolvedTypes).end(); it != end;
++it) {
const TypeReference &type = *it;
- Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
+ Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType());
+ if (type.type.isInlineComponentType() && !type.type.pendingResolutionName().isEmpty()) {
+ auto containingType = type.type.containingType();
+ auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName());
+ if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component
+ const QString typeName = stringAt(it.key());
+ int lastDot = typeName.lastIndexOf('.');
+
+ QList<QQmlError> errors = type.typeData ? type.typeData->errors() : QList<QQmlError>{};
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column));
+ error.setDescription(QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(typeName.leftRef(lastDot), type.type.pendingResolutionName()));
+ errors.prepend(error);
+ setError(errors);
+ return;
+ } else {
+ type.type.setInlineComponentObjectId(objectId);
+ }
+ }
if (type.typeData && type.typeData->isError()) {
const QString typeName = stringAt(it.key());
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(type.location.line);
- error.setColumn(type.location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column));
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
@@ -287,8 +391,8 @@ void QQmlTypeData::done()
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
error.setUrl(url());
- error.setLine(type.location.line);
- error.setColumn(type.location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column));
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
@@ -296,10 +400,28 @@ void QQmlTypeData::done()
}
}
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ m_typeClassName = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(finalUrl());
+ if (!m_typeClassName.isEmpty())
+ m_typeIds = QQmlMetaType::registerInternalCompositeType(m_typeClassName);
+
+ if (m_document) {
+ setupICs(m_document, &m_inlineComponentData, finalUrl());
+ } else {
+ setupICs(m_compiledData, &m_inlineComponentData, finalUrl());
+ }
+ auto typeCleanupGuard = qScopeGuard([&]() {
+ if (isError() && m_typeIds.isValid()) {
+ QQmlMetaType::unregisterInternalCompositeType(m_typeIds);
+ for (auto&& icData: qAsConst(m_inlineComponentData)) {
+ QQmlMetaType::unregisterInternalCompositeType(icData.typeIds);
+ }
+ }
+ });
+
QV4::ResolvedTypeReferenceMap resolvedTypeCache;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
{
- QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
+ QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
if (error.isValid()) {
setError(error);
qDeleteAll(resolvedTypeCache);
@@ -329,8 +451,12 @@ void QQmlTypeData::done()
if (!m_document.isNull()) {
// Compile component
compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
+ if (!isError())
+ setCompileUnit(m_document);
} else {
createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
+ if (!isError())
+ setCompileUnit(m_compiledData);
}
if (isError())
@@ -338,17 +464,18 @@ void QQmlTypeData::done()
{
QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine);
+ m_compiledData->inlineComponentData = m_inlineComponentData;
{
// Sanity check property bindings
QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData);
- QVector<QQmlJS::DiagnosticMessage> errors = validator.validate();
+ QVector<QQmlError> errors = validator.validate();
if (!errors.isEmpty()) {
setError(errors);
return;
}
}
- m_compiledData->finalizeCompositeType(enginePrivate);
+ m_compiledData->finalizeCompositeType(enginePrivate, typeIds());
}
{
@@ -376,6 +503,26 @@ void QQmlTypeData::done()
}
}
+ // associate inline components to root component
+ {
+ auto typeName = finalUrlString().splitRef('/').last().split('.').first().toString();
+ // typeName can be empty if a QQmlComponent was constructed with an empty QUrl parameter
+ if (!typeName.isEmpty() && typeName.at(0).isUpper() && !m_inlineComponentData.isEmpty()) {
+ QHashedStringRef const hashedStringRef { typeName };
+ QList<QQmlError> errors;
+ auto type = QQmlMetaType::typeForUrl(finalUrlString(), hashedStringRef, false, &errors);
+ Q_ASSERT(errors.empty());
+ if (type.isValid()) {
+ for (auto const &icDatum : m_inlineComponentData) {
+ Q_ASSERT(icDatum.typeIds.isValid());
+ QQmlType existingType = type.lookupInlineComponentById(type.lookupInlineComponentIdByName(m_compiledData->stringAt(icDatum.nameIndex)));
+ type.associateInlineComponent(m_compiledData->stringAt(icDatum.nameIndex),
+ icDatum.objectIndex, icDatum.typeIds, existingType);
+ }
+ }
+ }
+ }
+
{
// Collect imported scripts
m_compiledData->dependentScripts.reserve(m_scripts.count());
@@ -485,8 +632,8 @@ bool QQmlTypeData::loadFromSource()
for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) {
QQmlError e;
e.setUrl(url());
- e.setLine(msg.line);
- e.setColumn(msg.column);
+ e.setLine(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startLine));
+ e.setColumn(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startColumn));
e.setDescription(msg.message);
errors << e;
}
@@ -509,6 +656,22 @@ void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit)
void QQmlTypeData::continueLoadFromIR()
{
+ QQmlType containingType;
+ auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first();
+ int major = -1, minor = -1;
+ QQmlImportNamespace *ns = nullptr;
+ m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns);
+ for (auto const& object: m_document->objects) {
+ for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) {
+ QString const nameString = m_document->stringAt(it->nameIndex);
+ QByteArray const name = nameString.toUtf8();
+ auto importUrl = finalUrl();
+ importUrl.setFragment(QString::number(it->objectIndex));
+ auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance
+ m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType);
+ }
+ }
+
m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
m_importCache.setBaseUrl(finalUrl(), finalUrlString());
@@ -541,8 +704,8 @@ void QQmlTypeData::continueLoadFromIR()
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
return;
@@ -568,8 +731,8 @@ void QQmlTypeData::allDependenciesDone()
QQmlError error;
error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
errors.prepend(error);
}
}
@@ -677,8 +840,9 @@ void QQmlTypeData::resolveTypes()
if (ref.type.isCompositeSingleton()) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
- if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies || m_waitingOnMe.contains(ref.typeData.data())) {
- // TODO: give an error message? If so, we should record and show the path of the cycle.
+ if (ref.typeData->isWaiting() || m_waitingOnMe.contains(ref.typeData.data())) {
+ qWarning() << "Cyclic dependency detected between" << ref.typeData->urlString()
+ << "and" << urlString();
continue;
}
addDependency(ref.typeData.data());
@@ -700,15 +864,28 @@ void QQmlTypeData::resolveTypes()
const QString name = stringAt(unresolvedRef.key());
+ bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference;
+
if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line,
unresolvedRef->location.column, reportErrors,
- QQmlType::AnyRegistrationType) && reportErrors)
+ QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors)
return;
- if (ref.type.isComposite()) {
+ if (ref.type.isComposite() && !ref.selfReference) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
addDependency(ref.typeData.data());
}
+ if (ref.type.isInlineComponentType()) {
+ auto containingType = ref.type.containingType();
+ if (containingType.isValid()) {
+ auto const url = containingType.sourceUrl();
+ if (url.isValid()) {
+ auto typeData = typeLoader()->getType(url);
+ ref.typeData = typeData;
+ addDependency(typeData.data());
+ }
+ }
+ }
ref.majorVersion = majorVersion;
ref.minorVersion = minorVersion;
@@ -716,7 +893,6 @@ void QQmlTypeData::resolveTypes()
ref.location.column = unresolvedRef->location.column;
ref.needsCreation = unresolvedRef->needsCreation;
-
m_resolvedTypes.insert(unresolvedRef.key(), ref);
}
@@ -725,7 +901,7 @@ void QQmlTypeData::resolveTypes()
loadImplicitImport();
}
-QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches(
+QQmlError QQmlTypeData::buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
QV4::ResolvedTypeReferenceMap *resolvedTypeCache
) const
@@ -750,8 +926,40 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches(
if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) {
return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
}
- ref->compilationUnit = resolvedType->typeData->compilationUnit();
- } else if (qmlType.isValid()) {
+ ref->setCompilationUnit(resolvedType->typeData->compilationUnit());
+ if (resolvedType->type.isInlineComponentType()) {
+ // Inline component which is part of an already resolved type
+ int objectId = -1;
+ if (qmlType.containingType().isValid()) {
+ objectId = qmlType.containingType().lookupInlineComponentIdByName(QString::fromUtf8(qmlType.typeName()));
+ qmlType.setInlineComponentObjectId(objectId);
+ } else {
+ objectId = resolvedType->type.inlineComponendId();
+ }
+ Q_ASSERT(objectId != -1);
+ ref->typePropertyCache = resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId);
+ ref->type = qmlType;
+ Q_ASSERT(ref->type.isInlineComponentType());
+ }
+ } else if (resolvedType->type.isInlineComponentType()) {
+ // Inline component, defined in the file we are currently compiling
+ if (!m_inlineComponentToCompiledData.contains(resolvedType.key())) {
+ ref->type = qmlType;
+ if (qmlType.isValid()) {
+ // this is required for inline components in singletons
+ auto typeID = qmlType.lookupInlineComponentById(qmlType.inlineComponendId()).typeId();
+ auto exUnit = engine->obtainExecutableCompilationUnit(typeID);
+ if (exUnit) {
+ ref->setCompilationUnit(exUnit);
+ ref->typePropertyCache = engine->propertyCacheForType(typeID);
+ }
+ }
+ } else {
+ ref->setCompilationUnit(m_inlineComponentToCompiledData[resolvedType.key()]);
+ ref->typePropertyCache = m_inlineComponentToCompiledData[resolvedType.key()]->rootPropertyCache();
+ }
+
+ } else if (qmlType.isValid() && !resolvedType->selfReference) {
ref->type = qmlType;
Q_ASSERT(ref->type.isValid());
@@ -772,26 +980,29 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches(
ref->doDynamicTypeCheck();
resolvedTypeCache->insert(resolvedType.key(), ref.take());
}
- QQmlJS::DiagnosticMessage noError;
+ QQmlError noError;
return noError;
}
bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
TypeReference &ref, int lineNumber, int columnNumber,
- bool reportErrors, QQmlType::RegistrationType registrationType)
+ bool reportErrors, QQmlType::RegistrationType registrationType,
+ bool *typeRecursionDetected)
{
QQmlImportNamespace *typeNamespace = nullptr;
QList<QQmlError> errors;
bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
- &typeNamespace, &errors, registrationType);
+ &typeNamespace, &errors, registrationType,
+ typeRecursionDetected);
if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
// Lazy loading of implicit import
if (loadImplicitImport()) {
// Try again to find the type
errors.clear();
typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
- &typeNamespace, &errors, registrationType);
+ &typeNamespace, &errors, registrationType,
+ typeRecursionDetected);
} else {
return false; //loadImplicitImport() hit an error, and called setError already
}
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
index e1d0c900ea..906a0ee171 100644
--- a/src/qml/qml/qqmltypedata_p.h
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -69,6 +69,7 @@ public:
int majorVersion;
int minorVersion;
QQmlRefPointer<QQmlTypeData> typeData;
+ bool selfReference = false;
QString prefix; // used by CompositeSingleton types
QString qualifiedName() const;
bool needsCreation;
@@ -85,6 +86,8 @@ private:
friend class QQmlTypeLoader;
QQmlTypeData(const QUrl &, QQmlTypeLoader *);
+ template<typename Container>
+ void setCompileUnit(const Container &container);
public:
~QQmlTypeData() override;
@@ -92,6 +95,7 @@ public:
const QList<ScriptReference> &resolvedScripts() const;
QV4::ExecutableCompilationUnit *compilationUnit() const;
+ QV4::ExecutableCompilationUnit *compilationUnitForInlineComponent(unsigned int icObjectId) const;
// Used by QQmlComponent to get notifications
struct TypeDataCallback {
@@ -102,6 +106,9 @@ public:
void registerCallback(TypeDataCallback *);
void unregisterCallback(TypeDataCallback *);
+ CompositeMetaTypeIds typeIds(int objectId = 0) const;
+ QByteArray typeClassName() const { return m_typeClassName; }
+
protected:
void done() override;
void completed() override;
@@ -118,7 +125,7 @@ private:
void restoreIR(QV4::CompiledData::CompilationUnit &&unit);
void continueLoadFromIR();
void resolveTypes();
- QQmlJS::DiagnosticMessage buildTypeResolutionCaches(
+ QQmlError buildTypeResolutionCaches(
QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
QV4::ResolvedTypeReferenceMap *resolvedTypeCache
) const;
@@ -130,11 +137,11 @@ private:
bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
TypeReference &ref, int lineNumber = -1, int columnNumber = -1,
bool reportErrors = true,
- QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType);
+ QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType,
+ bool *typeRecursionDetected = nullptr);
void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
-
SourceCodeData m_backupSourceCode; // used when cache verification fails.
QScopedPointer<QmlIR::Document> m_document;
QV4::CompiledData::TypeReferenceMap m_typeReferences;
@@ -150,7 +157,16 @@ private:
QMap<int, TypeReference> m_resolvedTypes;
bool m_typesResolved:1;
- QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData;
+ // Used for self-referencing types, otherwise -1.
+ CompositeMetaTypeIds m_typeIds;
+ QByteArray m_typeClassName; // used for meta-object later
+
+ using ExecutableCompilationUnitPtr = QQmlRefPointer<QV4::ExecutableCompilationUnit>;
+
+ QHash<int, InlineComponentData> m_inlineComponentData;
+
+ ExecutableCompilationUnitPtr m_compiledData;
+ QHash<int, ExecutableCompilationUnitPtr> m_inlineComponentToCompiledData;
QList<TypeDataCallback *> m_callbacks;
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 6c12de926c..1d66e756fa 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -45,12 +45,15 @@
#include <private/qqmltypedata_p.h>
#include <private/qqmltypeloaderqmldircontent_p.h>
#include <private/qqmltypeloaderthread_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <QtQml/qqmlabstracturlinterceptor.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlextensioninterface.h>
#include <QtQml/qqmlfile.h>
+#include <qtqml_tracepoints_p.h>
+
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
@@ -391,23 +394,33 @@ QQmlEngine *QQmlTypeLoader::engine() const
return m_engine;
}
-/*!
+/*! \internal
Call the initializeEngine() method on \a iface. Used by QQmlImportDatabase to ensure it
gets called in the correct thread.
*/
-void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface,
- const char *uri)
+template<class Interface>
+void doInitializeEngine(Interface *iface, QQmlTypeLoaderThread *thread, QQmlEngine *engine,
+ const char *uri)
{
- Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
+ Q_ASSERT(thread->isThisThread() || engine->thread() == QThread::currentThread());
- if (m_thread->isThisThread()) {
- m_thread->initializeEngine(iface, uri);
+ if (thread->isThisThread()) {
+ thread->initializeEngine(iface, uri);
} else {
- Q_ASSERT(engine()->thread() == QThread::currentThread());
- iface->initializeEngine(engine(), uri);
+ Q_ASSERT(engine->thread() == QThread::currentThread());
+ iface->initializeEngine(engine, uri);
}
}
+void QQmlTypeLoader::initializeEngine(QQmlEngineExtensionInterface *iface, const char *uri)
+{
+ doInitializeEngine(iface, m_thread, engine(), uri);
+}
+
+void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface, const char *uri)
+{
+ doInitializeEngine(iface, m_thread, engine(), uri);
+}
void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
{
@@ -426,6 +439,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName)
void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d)
{
+ Q_TRACE_SCOPE(QQmlCompiling, blob->url());
QQmlCompilingProfiler prof(profiler(), blob);
blob->m_inCallback = true;
@@ -445,6 +459,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD
void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QV4::CompiledData::Unit *unit)
{
+ Q_TRACE_SCOPE(QQmlCompiling, blob->url());
QQmlCompilingProfiler prof(profiler(), blob);
blob->m_inCallback = true;
@@ -563,14 +578,10 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
QString qmldirFilePath;
QString qmldirUrl;
- if (QQmlMetaType::isLockedModule(import->uri, import->majorVersion)) {
- //Locked modules are checked first, to save on filesystem checks
- if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
- import->minorVersion, QString(), QString(), false, errors))
- return false;
-
- } else if (m_importCache.locateQmldir(importDatabase, import->uri, import->majorVersion, import->minorVersion,
- &qmldirFilePath, &qmldirUrl)) {
+ const QQmlImports::LocalQmldirResult qmldirResult = m_importCache.locateLocalQmldir(
+ importDatabase, import->uri, import->majorVersion, import->minorVersion,
+ &qmldirFilePath, &qmldirUrl);
+ if (qmldirResult == QQmlImports::QmldirFound) {
// This is a local library import
if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, qmldirFilePath, qmldirUrl, false, errors))
@@ -592,46 +603,57 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
scriptImported(blob, import->location, script.nameSpace, import->qualifier);
}
}
+ } else if (
+ // Major version of module already registered:
+ // We believe that the registration is complete.
+ QQmlMetaType::typeModule(import->uri, import->majorVersion)
+
+ // Otherwise, try to register further module types.
+ || (qmldirResult != QQmlImports::QmldirInterceptedToRemote
+ && QQmlMetaType::qmlRegisterModuleTypes(import->uri, import->majorVersion))
+
+ // Otherwise, there is no way to register any further types.
+ // Try with any module of that name.
+ || QQmlMetaType::isAnyModule(import->uri)) {
+
+ if (!m_importCache.addLibraryImport(
+ importDatabase, import->uri, import->qualifier, import->majorVersion,
+ import->minorVersion, QString(), QString(), false, errors)) {
+ return false;
+ }
} else {
- // Is this a module?
- if (QQmlMetaType::isAnyModule(import->uri)) {
+ // We haven't yet resolved this import
+ m_unresolvedImports << import;
+
+ QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor();
+
+ // Query any network import paths for this library.
+ // Interceptor might redirect local paths.
+ QStringList remotePathList = importDatabase->importPathList(
+ interceptor ? QQmlImportDatabase::LocalOrRemote
+ : QQmlImportDatabase::Remote);
+ if (!remotePathList.isEmpty()) {
+ // Add this library and request the possible locations for it
if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
- import->minorVersion, QString(), QString(), false, errors))
+ import->minorVersion, QString(), QString(), true, errors))
return false;
- } else {
- // We haven't yet resolved this import
- m_unresolvedImports << import;
-
- QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor();
-
- // Query any network import paths for this library.
- // Interceptor might redirect local paths.
- QStringList remotePathList = importDatabase->importPathList(
- interceptor ? QQmlImportDatabase::LocalOrRemote
- : QQmlImportDatabase::Remote);
- if (!remotePathList.isEmpty()) {
- // Add this library and request the possible locations for it
- if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
- import->minorVersion, QString(), QString(), true, errors))
- return false;
- // Probe for all possible locations
- int priority = 0;
- const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion);
- for (const QString &qmldirPath : qmlDirPaths) {
- if (interceptor) {
- QUrl url = interceptor->intercept(
- QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
- QQmlAbstractUrlInterceptor::QmldirFile);
- if (!QQmlFile::isLocalFile(url)
- && !fetchQmldir(url, import, ++priority, errors)) {
- return false;
- }
- } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
+ // Probe for all possible locations
+ int priority = 0;
+ const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion);
+ for (const QString &qmldirPath : qmlDirPaths) {
+ if (interceptor) {
+ QUrl url = interceptor->intercept(
+ QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
+ QQmlAbstractUrlInterceptor::QmldirFile);
+ if (!QQmlFile::isLocalFile(url)
+ && !fetchQmldir(url, import, ++priority, errors)) {
return false;
}
-
+ } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
+ return false;
}
+
}
}
}
@@ -675,8 +697,8 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
Q_ASSERT(errors.size());
QQmlError error(errors.takeFirst());
error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column));
errors.prepend(error); // put it back on the list after filling out information.
setError(errors);
}
@@ -705,7 +727,7 @@ bool QQmlTypeLoader::Blob::isDebugging() const
bool QQmlTypeLoader::Blob::diskCacheEnabled() const
{
- return (!disableDiskCache() || forceDiskCache()) && !isDebugging();
+ return (!disableDiskCache() && !isDebugging()) || forceDiskCache();
}
bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors)
@@ -964,48 +986,56 @@ bool QQmlTypeLoader::fileExists(const QString &path, const QString &file)
return false;
Q_ASSERT(path.endsWith(QLatin1Char('/')));
+
+ LockHolder<QQmlTypeLoader> holder(this);
+ QCache<QString, bool> *fileSet = m_importDirCache.object(path);
+ if (fileSet) {
+ if (bool *value = fileSet->object(file))
+ return *value;
+ } else if (m_importDirCache.contains(path)) {
+ // explicit nullptr in cache
+ return false;
+ }
+
+ auto addToCache = [&](const QFileInfo &fileInfo) {
+ if (!fileSet) {
+ fileSet = fileInfo.dir().exists() ? new QCache<QString, bool> : nullptr;
+ m_importDirCache.insert(path, fileSet);
+ if (!fileSet)
+ return false;
+ }
+
+ const bool exists = fileInfo.exists();
+ fileSet->insert(file, new bool(exists));
+ return exists;
+ };
+
if (path.at(0) == QLatin1Char(':')) {
// qrc resource
- QFileInfo fileInfo(path + file);
- return fileInfo.isFile();
- } else if (path.count() > 3 && path.at(3) == QLatin1Char(':') &&
- path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) {
+ return addToCache(QFileInfo(path + file));
+ }
+
+ if (path.count() > 3 && path.at(3) == QLatin1Char(':')
+ && path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) {
// qrc resource url
- QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file));
- return fileInfo.isFile();
+ return addToCache(QFileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)));
}
+
#if defined(Q_OS_ANDROID)
- else if (path.count() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/') &&
- path.startsWith(QLatin1String("assets"), Qt::CaseInsensitive)) {
+ if (path.count() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/')
+ && path.startsWith(QLatin1String("assets"), Qt::CaseInsensitive)) {
// assets resource url
- QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file));
- return fileInfo.isFile();
- } else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') &&
- path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) {
- // content url
- QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file));
- return fileInfo.isFile();
+ return addToCache(QFileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)));
}
-#endif
- LockHolder<QQmlTypeLoader> holder(this);
- if (!m_importDirCache.contains(path)) {
- bool exists = QDir(path).exists();
- QCache<QString, bool> *entry = exists ? new QCache<QString, bool> : nullptr;
- m_importDirCache.insert(path, entry);
+ if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/')
+ && path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) {
+ // content url
+ return addToCache(QFileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)));
}
- QCache<QString, bool> *fileSet = m_importDirCache.object(path);
- if (!fileSet)
- return false;
+#endif
- bool *value = fileSet->object(file);
- if (value) {
- return *value;
- } else {
- bool exists = QFile::exists(path + file);
- fileSet->insert(file, new bool(exists));
- return exists;
- }
+ return addToCache(QFileInfo(path + file));
}
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 38a7e05961..adecf61896 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE
class QQmlScriptBlob;
class QQmlQmldirData;
class QQmlTypeData;
+class QQmlEngineExtensionInterface;
class QQmlExtensionInterface;
class QQmlProfiler;
class QQmlTypeLoaderThread;
@@ -170,6 +171,7 @@ public:
void loadWithCachedUnit(QQmlDataBlob *blob, const QV4::CompiledData::Unit *unit, Mode mode = PreferSynchronous);
QQmlEngine *engine() const;
+ void initializeEngine(QQmlEngineExtensionInterface *, const char *);
void initializeEngine(QQmlExtensionInterface *, const char *);
void invalidate();
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
index 860971d296..714ea79e67 100644
--- a/src/qml/qml/qqmltypeloaderqmldircontent.cpp
+++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include <private/qqmltypeloaderqmldircontent_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
#include <QtQml/qqmlerror.h>
QT_BEGIN_NAMESPACE
@@ -59,8 +60,8 @@ QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const
for (const auto &parseError : parseErrors) {
QQmlError error;
error.setUrl(url);
- error.setLine(parseError.line);
- error.setColumn(parseError.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(parseError.loc.startLine));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(parseError.loc.startColumn));
error.setDescription(parseError.message);
errors.append(error);
}
@@ -83,8 +84,8 @@ void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QStr
void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error)
{
QQmlJS::DiagnosticMessage parseError;
- parseError.line = error.line();
- parseError.column = error.column();
+ parseError.loc.startLine = error.line();
+ parseError.loc.startColumn = error.column();
parseError.message = error.description();
m_parser.setError(parseError);
}
diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp
index 0e1cecd1e5..618bb09039 100644
--- a/src/qml/qml/qqmltypeloaderthread.cpp
+++ b/src/qml/qml/qqmltypeloaderthread.cpp
@@ -138,7 +138,13 @@ void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p)
void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
const char *uri)
{
- callMethodInMain(&This::initializeEngineMain, iface, uri);
+ callMethodInMain(&This::initializeExtensionMain, iface, uri);
+}
+
+void QQmlTypeLoaderThread::initializeEngine(QQmlEngineExtensionInterface *iface,
+ const char *uri)
+{
+ callMethodInMain(&This::initializeEngineExtensionMain, iface, uri);
}
void QQmlTypeLoaderThread::shutdownThread()
@@ -188,7 +194,14 @@ void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qrea
b->release();
}
-void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface,
+void QQmlTypeLoaderThread::initializeExtensionMain(QQmlExtensionInterface *iface,
+ const char *uri)
+{
+ Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
+ iface->initializeEngine(m_loader->engine(), uri);
+}
+
+void QQmlTypeLoaderThread::initializeEngineExtensionMain(QQmlEngineExtensionInterface *iface,
const char *uri)
{
Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
diff --git a/src/qml/qml/qqmltypeloaderthread_p.h b/src/qml/qml/qqmltypeloaderthread_p.h
index 67e47e86de..9fb441e6e2 100644
--- a/src/qml/qml/qqmltypeloaderthread_p.h
+++ b/src/qml/qml/qqmltypeloaderthread_p.h
@@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE
class QQmlDataBlob;
class QQmlTypeLoader;
+class QQmlEngineExtensionInterface;
class QQmlExtensionInterface;
class QQmlTypeLoaderThread : public QQmlThread
@@ -86,6 +87,7 @@ public:
void callCompleted(QQmlDataBlob *b);
void callDownloadProgressChanged(QQmlDataBlob *b, qreal p);
void initializeEngine(QQmlExtensionInterface *, const char *);
+ void initializeEngine(QQmlEngineExtensionInterface *, const char *);
protected:
void shutdownThread() override;
@@ -96,7 +98,8 @@ private:
void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
void callCompletedMain(QQmlDataBlob *b);
void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p);
- void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
+ void initializeExtensionMain(QQmlExtensionInterface *iface, const char *uri);
+ void initializeEngineExtensionMain(QQmlEngineExtensionInterface *iface, const char *uri);
QQmlTypeLoader *m_loader;
#if QT_CONFIG(qml_network)
diff --git a/src/qml/qml/qqmltypemodule.cpp b/src/qml/qml/qqmltypemodule.cpp
index 9c9bf3e48f..9d6f269030 100644
--- a/src/qml/qml/qqmltypemodule.cpp
+++ b/src/qml/qml/qqmltypemodule.cpp
@@ -97,10 +97,14 @@ void QQmlTypeModule::add(QQmlTypePrivate *type)
QList<QQmlTypePrivate *> &list = d->typeHash[type->elementName];
for (int ii = 0; ii < list.count(); ++ii) {
- Q_ASSERT(list.at(ii));
- if (list.at(ii)->version_min < type->version_min) {
+ QQmlTypePrivate *in_list = list.at(ii);
+ Q_ASSERT(in_list);
+ if (in_list->version_min < type->version_min) {
list.insert(ii, type);
return;
+ } else if (in_list->version_min == type->version_min) {
+ list[ii] = type;
+ return;
}
}
list.append(type);
diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp
index 8f1a61e6ad..1015403226 100644
--- a/src/qml/qml/qqmltypenamecache.cpp
+++ b/src/qml/qml/qqmltypenamecache.cpp
@@ -154,8 +154,10 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, QQml
QQmlImportNamespace *typeNamespace = nullptr;
QList<QQmlError> errors;
QQmlType t;
+ bool typeRecursionDetected = false;
bool typeFound = m_imports.resolveType(typeName, &t, nullptr, nullptr, &typeNamespace, &errors,
- QQmlType::AnyRegistrationType, recursionRestriction);
+ QQmlType::AnyRegistrationType,
+ recursionRestriction == QQmlImport::AllowRecursion ? &typeRecursionDetected : nullptr);
if (typeFound) {
return Result(t);
}
diff --git a/src/qml/qml/qqmltypenotavailable.cpp b/src/qml/qml/qqmltypenotavailable.cpp
index ffa4472e4b..0e95d6062c 100644
--- a/src/qml/qml/qqmltypenotavailable.cpp
+++ b/src/qml/qml/qqmltypenotavailable.cpp
@@ -46,8 +46,6 @@ int qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMi
return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(uri,versionMajor,versionMinor,qmlName,message);
}
-QQmlTypeNotAvailable::QQmlTypeNotAvailable() { }
-
QT_END_NAMESPACE
#include "moc_qqmltypenotavailable_p.cpp"
diff --git a/src/qml/qml/qqmltypenotavailable_p.h b/src/qml/qml/qqmltypenotavailable_p.h
index 33630bf507..d8427f2961 100644
--- a/src/qml/qml/qqmltypenotavailable_p.h
+++ b/src/qml/qml/qqmltypenotavailable_p.h
@@ -55,11 +55,11 @@
QT_BEGIN_NAMESPACE
-
class QQmlTypeNotAvailable : public QObject {
Q_OBJECT
-public:
- QQmlTypeNotAvailable();
+ QML_NAMED_ELEMENT(TypeNotAvailable)
+ QML_UNCREATABLE("Type not available.")
+ QML_ADDED_IN_MINOR_VERSION(15)
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index ef4a628a04..fa5d36503d 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -400,7 +400,7 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
// can only compare a QObject* against a QML type
const QObjectWrapper *wrapper = var.as<QObjectWrapper>();
if (!wrapper)
- return engine->throwTypeError();
+ return QV4::Encode(false);
// in case the wrapper outlived the QObject*
const QObject *wrapperObject = wrapper->object();
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index a8fe4ec2c6..a86564a49a 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -48,6 +48,8 @@
#include <private/qqmlmodelindexvaluetype_p.h>
#endif
+Q_DECLARE_METATYPE(QQmlProperty)
+
QT_BEGIN_NAMESPACE
namespace {
@@ -62,7 +64,7 @@ struct QQmlValueTypeFactoryImpl
const QMetaObject *metaObjectForMetaType(int);
QQmlValueType *valueType(int);
- QQmlValueType *valueTypes[QVariant::UserType];
+ QQmlValueType *valueTypes[QMetaType::User];
QHash<int, QQmlValueType *> userTypes;
QMutex mutex;
@@ -71,7 +73,7 @@ struct QQmlValueTypeFactoryImpl
QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl()
{
- std::fill_n(valueTypes, int(QVariant::UserType), &invalidValueType);
+ std::fill_n(valueTypes, int(QMetaType::User), &invalidValueType);
#if QT_CONFIG(qml_itemmodel)
// See types wrapped in qqmlmodelindexvaluetype_p.h
@@ -118,24 +120,24 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx)
const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
{
switch (t) {
- case QVariant::Point:
+ case QMetaType::QPoint:
return &QQmlPointValueType::staticMetaObject;
- case QVariant::PointF:
+ case QMetaType::QPointF:
return &QQmlPointFValueType::staticMetaObject;
- case QVariant::Size:
+ case QMetaType::QSize:
return &QQmlSizeValueType::staticMetaObject;
- case QVariant::SizeF:
+ case QMetaType::QSizeF:
return &QQmlSizeFValueType::staticMetaObject;
- case QVariant::Rect:
+ case QMetaType::QRect:
return &QQmlRectValueType::staticMetaObject;
- case QVariant::RectF:
+ case QMetaType::QRectF:
return &QQmlRectFValueType::staticMetaObject;
- case QVariant::EasingCurve:
+ case QMetaType::QEasingCurve:
return &QQmlEasingValueType::staticMetaObject;
#if QT_CONFIG(qml_itemmodel)
- case QVariant::ModelIndex:
+ case QMetaType::QModelIndex:
return &QQmlModelIndexValueType::staticMetaObject;
- case QVariant::PersistentModelIndex:
+ case QMetaType::QPersistentModelIndex:
return &QQmlPersistentModelIndexValueType::staticMetaObject;
#endif
default:
@@ -143,7 +145,8 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
if (t == qMetaTypeId<QItemSelectionRange>())
return &QQmlItemSelectionRangeValueType::staticMetaObject;
#endif
-
+ if (t == qMetaTypeId<QQmlProperty>())
+ return &QQmlPropertyValueType::staticMetaObject;
if (const QMetaObject *mo = QQml_valueTypeProvider()->metaObjectForMetaType(t))
return mo;
break;
@@ -157,7 +160,7 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx)
{
- if (idx >= (int)QVariant::UserType) {
+ if (idx >= (int)QMetaType::User) {
// Protect the hash with a mutex
mutex.lock();
@@ -598,6 +601,16 @@ void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant)
v = newEasingCurve;
}
+QObject *QQmlPropertyValueType::object() const
+{
+ return v.object();
+}
+
+QString QQmlPropertyValueType::name() const
+{
+ return v.name();
+}
+
QVariantList QQmlEasingValueType::bezierCurve() const
{
QVariantList rv;
diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h
index fec942fcbb..29fa999725 100644
--- a/src/qml/qml/qqmlvaluetype_p.h
+++ b/src/qml/qml/qqmlvaluetype_p.h
@@ -242,6 +242,8 @@ struct QQmlEasingValueType
{
QEasingCurve v;
Q_GADGET
+ QML_NAMED_ELEMENT(Easing)
+ QML_UNCREATABLE("Use the Type enum.")
Q_PROPERTY(QQmlEasingValueType::Type type READ type WRITE setType FINAL)
Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL)
@@ -289,6 +291,17 @@ public:
QVariantList bezierCurve() const;
};
+struct QQmlPropertyValueType
+{
+ QQmlProperty v;
+ Q_PROPERTY(QObject *object READ object CONSTANT FINAL)
+ Q_PROPERTY(QString name READ name CONSTANT FINAL)
+ Q_GADGET
+public:
+ QObject *object() const;
+ QString name() const;
+};
+
template<typename T>
int qmlRegisterValueTypeEnums(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index cf6553d129..110288faf7 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -96,29 +96,29 @@ using namespace QV4;
void Heap::QQmlValueTypeWrapper::destroy()
{
- if (gadgetPtr) {
- valueType->metaType.destruct(gadgetPtr);
- ::operator delete(gadgetPtr);
+ if (m_gadgetPtr) {
+ m_valueType->metaType.destruct(m_gadgetPtr);
+ ::operator delete(m_gadgetPtr);
}
- if (_propertyCache)
- _propertyCache->release();
+ if (m_propertyCache)
+ m_propertyCache->release();
Object::destroy();
}
void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const
{
- Q_ASSERT(valueType->metaType.id() == value.userType());
- if (gadgetPtr)
- valueType->metaType.destruct(gadgetPtr);
- if (!gadgetPtr)
- gadgetPtr = ::operator new(valueType->metaType.sizeOf());
- valueType->metaType.construct(gadgetPtr, value.constData());
+ Q_ASSERT(valueType()->metaType.id() == value.userType());
+ if (auto *gadget = gadgetPtr())
+ valueType()->metaType.destruct(gadget);
+ if (!gadgetPtr())
+ setGadgetPtr(::operator new(valueType()->metaType.sizeOf()));
+ valueType()->metaType.construct(gadgetPtr(), value.constData());
}
QVariant Heap::QQmlValueTypeWrapper::toVariant() const
{
- Q_ASSERT(gadgetPtr);
- return QVariant(valueType->metaType.id(), gadgetPtr);
+ Q_ASSERT(gadgetPtr());
+ return QVariant(valueType()->metaType.id(), gadgetPtr());
}
@@ -146,13 +146,13 @@ bool QQmlValueTypeReference::readReferenceValue() const
QQmlPropertyCache *cache = nullptr;
if (const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(variantReferenceType))
cache = QJSEnginePrivate::get(engine())->cache(mo);
- if (d()->gadgetPtr) {
- d()->valueType->metaType.destruct(d()->gadgetPtr);
- ::operator delete(d()->gadgetPtr);
+ if (d()->gadgetPtr()) {
+ d()->valueType()->metaType.destruct(d()->gadgetPtr());
+ ::operator delete(d()->gadgetPtr());
}
- d()->gadgetPtr =nullptr;
+ d()->setGadgetPtr(nullptr);
d()->setPropertyCache(cache);
- d()->valueType = QQmlValueTypeFactory::valueType(variantReferenceType);
+ d()->setValueType(QQmlValueTypeFactory::valueType(variantReferenceType));
if (!cache)
return false;
} else {
@@ -161,12 +161,12 @@ bool QQmlValueTypeReference::readReferenceValue() const
}
d()->setValue(variantReferenceValue);
} else {
- if (!d()->gadgetPtr) {
- d()->gadgetPtr = ::operator new(d()->valueType->metaType.sizeOf());
- d()->valueType->metaType.construct(d()->gadgetPtr, nullptr);
+ if (!d()->gadgetPtr()) {
+ d()->setGadgetPtr(::operator new(d()->valueType()->metaType.sizeOf()));
+ d()->valueType()->metaType.construct(d()->gadgetPtr(), nullptr);
}
// value-type reference
- void *args[] = { d()->gadgetPtr, nullptr };
+ void *args[] = { d()->gadgetPtr(), nullptr };
QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, args);
}
return true;
@@ -192,8 +192,13 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj
r->d()->object = object;
r->d()->property = property;
r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
- r->d()->valueType = QQmlValueTypeFactory::valueType(typeId);
- r->d()->gadgetPtr = nullptr;
+ auto valueType = QQmlValueTypeFactory::valueType(typeId);
+ if (!valueType) {
+ return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
+ .arg(QString::fromUtf8(QMetaType(typeId).name())));
+ }
+ r->d()->setValueType(valueType);
+ r->d()->setGadgetPtr(nullptr);
return r->asReturnedValue();
}
@@ -204,8 +209,13 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria
Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>());
r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
- r->d()->valueType = QQmlValueTypeFactory::valueType(typeId);
- r->d()->gadgetPtr = nullptr;
+ auto valueType = QQmlValueTypeFactory::valueType(typeId);
+ if (!valueType) {
+ return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
+ .arg(QString::fromUtf8(QMetaType(typeId).name())));
+ }
+ r->d()->setValueType(valueType);
+ r->d()->setGadgetPtr(nullptr);
r->d()->setValue(value);
return r->asReturnedValue();
}
@@ -223,9 +233,9 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const
if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())
if (!ref->readReferenceValue())
return false;
- const int typeId = d()->valueType->metaType.id();
+ const int typeId = d()->valueType()->metaType.id();
QMetaType::destruct(typeId, data);
- QMetaType::construct(typeId, data, d()->gadgetPtr);
+ QMetaType::construct(typeId, data, d()->gadgetPtr());
return true;
}
@@ -307,7 +317,7 @@ bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
int QQmlValueTypeWrapper::typeId() const
{
- return d()->valueType->metaType.id();
+ return d()->valueType()->metaType.id();
}
bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
@@ -315,10 +325,10 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
bool destructGadgetOnExit = false;
Q_ALLOCA_DECLARE(void, gadget);
if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) {
- if (!d()->gadgetPtr) {
- Q_ALLOCA_ASSIGN(void, gadget, d()->valueType->metaType.sizeOf());
- d()->gadgetPtr = gadget;
- d()->valueType->metaType.construct(d()->gadgetPtr, nullptr);
+ if (!d()->gadgetPtr()) {
+ Q_ALLOCA_ASSIGN(void, gadget, d()->valueType()->metaType.sizeOf());
+ d()->setGadgetPtr(gadget);
+ d()->valueType()->metaType.construct(d()->gadgetPtr(), nullptr);
destructGadgetOnExit = true;
}
if (!ref->readReferenceValue())
@@ -327,12 +337,12 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
int flags = 0;
int status = -1;
- void *a[] = { d()->gadgetPtr, nullptr, &status, &flags };
+ void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
if (destructGadgetOnExit) {
- d()->valueType->metaType.destruct(d()->gadgetPtr);
- d()->gadgetPtr = nullptr;
+ d()->valueType()->metaType.destruct(d()->gadgetPtr());
+ d()->setGadgetPtr(nullptr);
}
return true;
}
@@ -354,16 +364,16 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
// Prepare a buffer to pass to QMetaType::convert()
QString convertResult;
convertResult.~QString();
- if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->metaType.id(), &convertResult, QMetaType::QString)) {
+ if (QMetaType::convert(w->d()->gadgetPtr(), w->d()->valueType()->metaType.id(), &convertResult, QMetaType::QString)) {
result = convertResult;
} else {
- result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->metaType.id()))
+ result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType()->metaType.id()))
+ QLatin1Char('(');
const QMetaObject *mo = w->d()->propertyCache()->metaObject();
const int propCount = mo->propertyCount();
for (int i = 0; i < propCount; ++i) {
if (mo->property(i).isDesignable()) {
- QVariant value = mo->property(i).readOnGadget(w->d()->gadgetPtr);
+ QVariant value = mo->property(i).readOnGadget(w->d()->gadgetPtr());
if (i > 0)
result += QLatin1String(", ");
result += value.toString();
@@ -387,7 +397,7 @@ Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
if (property->propType() == metatype) { \
cpptype v; \
void *args[] = { &v, nullptr }; \
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), \
QMetaObject::ReadProperty, index, args); \
return QV4::Encode(constructor(v)); \
}
@@ -412,7 +422,7 @@ Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
v = QVariant(property->propType(), static_cast<void *>(nullptr));
args[0] = v.data();
}
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty,
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty,
index, args);
return engine->fromVariant(v);
#undef VALUE_TYPE_LOAD
@@ -601,10 +611,10 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QVariant v = v4->toVariant(value, property.userType());
- if (property.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
+ if (property.isEnumType() && (QMetaType::Type)v.userType() == QMetaType::Double)
v = v.toInt();
- void *gadget = r->d()->gadgetPtr;
+ void *gadget = r->d()->gadgetPtr();
property.writeOnGadget(gadget, v);
@@ -620,7 +630,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
} else {
int flags = 0;
int status = -1;
- void *a[] = { r->d()->gadgetPtr, nullptr, &status, &flags };
+ void *a[] = { r->d()->gadgetPtr(), nullptr, &status, &flags };
QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a);
}
}
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index baac129afa..60079aa623 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -69,22 +69,45 @@ namespace Heap {
struct QQmlValueTypeWrapper : Object {
void init() { Object::init(); }
void destroy();
- QQmlPropertyCache *propertyCache() const { return _propertyCache; }
+
+ QQmlPropertyCache *propertyCache() const { return m_propertyCache; }
void setPropertyCache(QQmlPropertyCache *c) {
if (c)
c->addref();
- if (_propertyCache)
- _propertyCache->release();
- _propertyCache = c;
+ if (m_propertyCache)
+ m_propertyCache->release();
+ m_propertyCache = c;
+ }
+
+ void setValueType(QQmlValueType *valueType)
+ {
+ Q_ASSERT(valueType != nullptr);
+ m_valueType = valueType;
+ }
+
+ QQmlValueType *valueType() const
+ {
+ Q_ASSERT(m_valueType != nullptr);
+ return m_valueType;
+ }
+
+ void setGadgetPtr(void *gadgetPtr) const
+ {
+ m_gadgetPtr = gadgetPtr;
+ }
+
+ void *gadgetPtr() const
+ {
+ return m_gadgetPtr;
}
- mutable void *gadgetPtr;
- QQmlValueType *valueType;
void setValue(const QVariant &value) const;
QVariant toVariant() const;
private:
- QQmlPropertyCache *_propertyCache;
+ mutable void *m_gadgetPtr;
+ QQmlValueType *m_valueType;
+ QQmlPropertyCache *m_propertyCache;
};
}
diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h
index 13c5524d96..05a78a17fe 100644
--- a/src/qml/qml/qqmlvme_p.h
+++ b/src/qml/qml/qqmlvme_p.h
@@ -64,6 +64,8 @@
#include <private/qqmlengine_p.h>
#include <private/qfinitestack_p.h>
+#include <atomic>
+
QT_BEGIN_NAMESPACE
class QObject;
@@ -97,17 +99,20 @@ class QTypeInfo<QQmlVMETypes::State> : public QTypeInfoMerger<QQmlVMETypes::Stat
class QQmlInstantiationInterrupt {
public:
inline QQmlInstantiationInterrupt();
- inline QQmlInstantiationInterrupt(volatile bool *runWhile, int nsecs=0);
- inline QQmlInstantiationInterrupt(int nsecs);
+ // ### Qt 6: remove
+ inline QQmlInstantiationInterrupt(volatile bool *runWhile, qint64 nsecs=0);
+ inline QQmlInstantiationInterrupt(std::atomic<bool> *runWhile, qint64 nsecs = 0);
+ inline QQmlInstantiationInterrupt(qint64 nsecs);
inline void reset();
inline bool shouldInterrupt() const;
private:
- enum Mode { None, Time, Flag };
+ enum Mode { None, Time, LegacyFlag, Flag }; // ### Qt 6: remove LegacyFlag
Mode mode;
QElapsedTimer timer;
- int nsecs;
- volatile bool *runWhile;
+ qint64 nsecs = 0;
+ volatile bool *runWhileLegacy = nullptr; // ### Qt 6: remove
+ std::atomic<bool> *runWhile = nullptr;
};
class Q_QML_PRIVATE_EXPORT QQmlVME
@@ -147,17 +152,22 @@ private:
};
QQmlInstantiationInterrupt::QQmlInstantiationInterrupt()
- : mode(None), nsecs(0), runWhile(nullptr)
+ : mode(None)
{
}
-QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(volatile bool *runWhile, int nsecs)
+QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(volatile bool *runWhile, qint64 nsecs)
+ : mode(LegacyFlag), nsecs(nsecs), runWhileLegacy(runWhile)
+{
+}
+
+QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(std::atomic<bool> *runWhile, qint64 nsecs)
: mode(Flag), nsecs(nsecs), runWhile(runWhile)
{
}
-QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(int nsecs)
- : mode(Time), nsecs(nsecs), runWhile(nullptr)
+QQmlInstantiationInterrupt::QQmlInstantiationInterrupt(qint64 nsecs)
+ : mode(Time), nsecs(nsecs)
{
}
@@ -169,15 +179,18 @@ void QQmlInstantiationInterrupt::reset()
bool QQmlInstantiationInterrupt::shouldInterrupt() const
{
- if (mode == None) {
+ switch (mode) {
+ case None:
return false;
- } else if (mode == Time) {
+ case Time:
return timer.nsecsElapsed() > nsecs;
- } else if (mode == Flag) {
- return !*runWhile || (nsecs && timer.nsecsElapsed() > nsecs);
- } else {
- return false;
+ case LegacyFlag:
+ return !*runWhileLegacy || (nsecs && timer.nsecsElapsed() > nsecs);
+ case Flag:
+ return !runWhile->load(std::memory_order_acquire) || (nsecs && timer.nsecsElapsed() > nsecs);
}
+ Q_UNREACHABLE();
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 69215b1304..aa9f4bc1bd 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -48,6 +48,7 @@
#include "qqmlcontext_p.h"
#include "qqmlbinding_p.h"
#include "qqmlpropertyvalueinterceptor_p.h"
+#include <qqmlinfo.h>
#include <private/qqmlglobal_p.h>
@@ -60,32 +61,105 @@
#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmlpropertycachemethodarguments_p.h>
+#include <climits> // for CHAR_BIT
+
QT_BEGIN_NAMESPACE
+class ResolvedList
+{
+ Q_DISABLE_COPY_MOVE(ResolvedList)
+
+public:
+ ResolvedList(QQmlListProperty<QObject> *prop)
+ {
+ // see QQmlVMEMetaObject::metaCall for how this was constructed
+ auto encodedIndex = quintptr(prop->data);
+ constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
+ quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
+ m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
+
+ // walk up to the correct meta object if necessary
+ auto mo = prop->object->metaObject();
+ while (inheritanceDepth--)
+ mo = mo->superClass();
+ m_metaObject = static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(mo));
+ Q_ASSERT(m_metaObject);
+ Q_ASSERT( ::strstr(m_metaObject->property(m_metaObject->propOffset() + m_id).typeName(), "QQmlListProperty") );
+ Q_ASSERT(m_metaObject->object == prop->object);
+
+ // readPropertyAsList() with checks transformed into Q_ASSERT
+ // and without allocation.
+ if (m_metaObject->propertyAndMethodStorage.isUndefined() &&
+ m_metaObject->propertyAndMethodStorage.valueRef()) {
+ return;
+ }
+
+ if (auto *md = static_cast<QV4::MemberData *>(
+ m_metaObject->propertyAndMethodStorage.asManaged())) {
+ const auto *v = (md->data() + m_id)->as<QV4::VariantObject>();
+ Q_ASSERT(v);
+ Q_ASSERT(v->d());
+ QVariant &data = v->d()->data();
+ Q_ASSERT(data.userType() == qMetaTypeId<QVector<QQmlGuard<QObject>>>());
+ m_list = static_cast<QVector<QQmlGuard<QObject>> *>(data.data());
+ Q_ASSERT(m_list);
+ }
+ }
+
+ ~ResolvedList() = default;
+
+ QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
+ QVector<QQmlGuard<QObject>> *list() const { return m_list; }
+ quintptr id() const { return m_id; }
+
+ void activateSignal() const
+ {
+ m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()),
+ nullptr);
+ }
+
+private:
+ QQmlVMEMetaObject *m_metaObject = nullptr;
+ QVector<QQmlGuard<QObject>> *m_list = nullptr;
+ quintptr m_id = 0;
+};
+
static void list_append(QQmlListProperty<QObject> *prop, QObject *o)
{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- list->append(o);
- static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), nullptr);
+ const ResolvedList resolved(prop);
+ resolved.list()->append(o);
+ resolved.activateSignal();
}
static int list_count(QQmlListProperty<QObject> *prop)
{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- return list->count();
+ return ResolvedList(prop).list()->count();
}
static QObject *list_at(QQmlListProperty<QObject> *prop, int index)
{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- return list->at(index);
+ return ResolvedList(prop).list()->at(index);
}
static void list_clear(QQmlListProperty<QObject> *prop)
{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- list->clear();
- static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), nullptr);
+ const ResolvedList resolved(prop);
+ resolved.list()->clear();
+ resolved.activateSignal();
+}
+
+static void list_replace(QQmlListProperty<QObject> *prop, int index, QObject *o)
+{
+ const ResolvedList resolved(prop);
+ resolved.list()->replace(index, o);
+ resolved.activateSignal();
+}
+
+static void list_removeLast(QQmlListProperty<QObject> *prop)
+{
+ const ResolvedList resolved(prop);
+ resolved.list()->removeLast();
+ resolved.activateSignal();
}
QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr()
@@ -244,7 +318,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a)
const QQmlData *data = QQmlData::get(object);
const int type = data->propertyCache->property(id)->propType();
- if (type != QVariant::Invalid) {
+ if (type != QMetaType::UnknownType) {
if (valueIndex != -1) {
QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
data->context->engine, type);
@@ -488,7 +562,7 @@ QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) const
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data().type() != QVariant::Url)
+ if (!v || v->d()->data().userType() != QMetaType::QUrl)
return QUrl();
return v->d()->data().value<QUrl>();
}
@@ -502,7 +576,7 @@ QDate QQmlVMEMetaObject::readPropertyAsDate(int id) const
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data().type() != QVariant::Date)
+ if (!v || v->d()->data().userType() != QMetaType::QDate)
return QDate();
return v->d()->data().value<QDate>();
}
@@ -516,7 +590,7 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id)
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data().type() != QVariant::DateTime)
+ if (!v || v->d()->data().userType() != QMetaType::QDateTime)
return QDateTime();
return v->d()->data().value<QDateTime>();
}
@@ -530,7 +604,7 @@ QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data().type() != QVariant::SizeF)
+ if (!v || v->d()->data().userType() != QMetaType::QSizeF)
return QSizeF();
return v->d()->data().value<QSizeF>();
}
@@ -544,7 +618,7 @@ QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) const
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data().type() != QVariant::PointF)
+ if (!v || v->d()->data().userType() != QMetaType::QPointF)
return QPointF();
return v->d()->data().value<QPointF>();
}
@@ -563,7 +637,7 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const
return wrapper->object();
}
-QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const
+QVector<QQmlGuard<QObject>> *QQmlVMEMetaObject::readPropertyAsList(int id) const
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
@@ -571,12 +645,12 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const
QV4::Scope scope(engine);
QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id));
- if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) {
- QVariant variant(QVariant::fromValue(QList<QObject*>()));
+ if (!v || (int)v->d()->data().userType() != qMetaTypeId<QVector<QQmlGuard<QObject>> >()) {
+ QVariant variant(QVariant::fromValue(QVector<QQmlGuard<QObject>>()));
v = engine->newVariantObject(variant);
md->set(engine, id, v);
}
- return static_cast<QList<QObject *> *>(v->d()->data().data());
+ return static_cast<QVector<QQmlGuard<QObject>> *>(v->d()->data().data());
}
QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const
@@ -588,7 +662,7 @@ QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const
QV4::Scope scope(engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data().type() != QVariant::RectF)
+ if (!v || v->d()->data().userType() != QMetaType::QRectF)
return QRectF();
return v->d()->data().value<QRectF>();
}
@@ -685,13 +759,37 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
break;
case QV4::CompiledData::BuiltinType::InvalidBuiltin:
if (property.isList) {
- QList<QObject *> *list = readPropertyAsList(id);
- QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]);
- *p = QQmlListProperty<QObject>(object, list,
- list_append, list_count, list_at,
- list_clear);
- p->dummy1 = this;
- p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id));
+ // when reading from the list, we need to find the correct MetaObject,
+ // namely this. However, obejct->metaObject might point to any MetaObject
+ // down the inheritance hierarchy, so we need to store how far we have
+ // to go down
+ // To do this, we encode the hierarchy depth together with the id of the
+ // property in a single quintptr, with the first half storing the depth
+ // and the second half storing the property id
+ auto mo = object->metaObject();
+ quintptr inheritanceDepth = 0u;
+ while (mo && mo != this) {
+ mo = mo->superClass();
+ ++inheritanceDepth;
+ }
+ constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
+ if (Q_UNLIKELY(inheritanceDepth >= (quintptr(1) << quintptr(usableBits / 2u) ) )) {
+ qmlWarning(object) << "Too many objects in inheritance hierarchy for list property";
+ return -1;
+ }
+ if (Q_UNLIKELY(quintptr(id) >= (quintptr(1) << quintptr(usableBits / 2) ) )) {
+ qmlWarning(object) << "Too many properties in object for list property";
+ return -1;
+ }
+ quintptr encodedIndex = (inheritanceDepth << (usableBits/2)) + id;
+
+
+ readPropertyAsList(id); // Initializes if necessary
+ *static_cast<QQmlListProperty<QObject> *>(a[0])
+ = QQmlListProperty<QObject>(
+ object, reinterpret_cast<void *>(quintptr(encodedIndex)),
+ list_append, list_count, list_at,
+ list_clear, list_replace, list_removeLast);
} else {
*reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id);
}
@@ -842,7 +940,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
if (c == QMetaObject::WriteProperty)
- valueType->write(target, coreIndex, nullptr);
+ valueType->write(target, coreIndex, {});
return rv;
} else {
@@ -1140,7 +1238,7 @@ void QQmlVMEMetaObject::ensureQObjectWrapper()
void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack)
{
- if (engine != markStack->engine)
+ if (engine != markStack->engine())
return;
propertyAndMethodStorage.markOnce(markStack);
@@ -1162,6 +1260,8 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex,
const int aliasId = index - propOffset() - compiledObject->nProperties;
const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
+ while (aliasData->aliasToLocalAlias)
+ aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
*target = ctxt->idValues[aliasData->targetObjectId].data();
if (!*target)
return false;
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 58332ff7a8..e17701a968 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -188,7 +188,7 @@ public:
QDateTime readPropertyAsDateTime(int id);
QRectF readPropertyAsRectF(int id) const;
QObject *readPropertyAsQObject(int id) const;
- QList<QObject *> *readPropertyAsList(int id) const;
+ QVector<QQmlGuard<QObject> > *readPropertyAsList(int id) const;
void writeProperty(int id, int v);
void writeProperty(int id, bool v);
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 021aa369fa..86e6282e53 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -54,6 +54,7 @@
#include <QtCore/qobject.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qjsengine.h>
+#include <QtQml/qqmlfile.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtCore/qtextcodec.h>
#include <QtCore/qxmlstream.h>
@@ -77,6 +78,8 @@ using namespace QV4;
QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP);
+DEFINE_BOOL_CONFIG_OPTION(xhrFileWrite, QML_XHR_ALLOW_FILE_WRITE);
+DEFINE_BOOL_CONFIG_OPTION(xhrFileRead, QML_XHR_ALLOW_FILE_READ);
struct QQmlXMLHttpRequestData {
QQmlXMLHttpRequestData();
@@ -1195,6 +1198,37 @@ void QQmlXMLHttpRequest::fillHeadersList()
void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
{
QNetworkRequest request = m_request;
+
+ if (QQmlFile::isLocalFile(url)) {
+ if (m_method == QLatin1String("PUT"))
+ {
+ if (!xhrFileWrite()) {
+ if (qEnvironmentVariableIsSet("QML_XHR_ALLOW_FILE_WRITE")) {
+ qWarning("XMLHttpRequest: Tried to use PUT on a local file despite being disabled.");
+ return;
+ } else {
+ qWarning("XMLHttpRequest: Using PUT on a local file is dangerous "
+ "and will be disabled by default in a future Qt version."
+ "Set QML_XHR_ALLOW_FILE_WRITE to 1 if you wish to continue using this feature.");
+ }
+ }
+ } else if (m_method == QLatin1String("GET")) {
+ if (!xhrFileRead()) {
+ if (qEnvironmentVariableIsSet("QML_XHR_ALLOW_FILE_READ")) {
+ qWarning("XMLHttpRequest: Tried to use GET on a local file despite being disabled.");
+ return;
+ } else {
+ qWarning("XMLHttpRequest: Using GET on a local file is dangerous "
+ "and will be disabled by default in a future Qt version."
+ "Set QML_XHR_ALLOW_FILE_READ to 1 if you wish to continue using this feature.");
+ }
+ }
+ } else {
+ qWarning("XMLHttpRequest: Unsupported method used on a local file");
+ return;
+ }
+ }
+
request.setUrl(url);
if(m_method == QLatin1String("POST") ||
m_method == QLatin1String("PUT")) {
@@ -1266,7 +1300,7 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
} else {
QObject::connect(m_network, SIGNAL(readyRead()),
this, SLOT(readyRead()));
- QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)),
+ QObject::connect(m_network, SIGNAL(errorOccurred(QNetworkReply::NetworkError)),
this, SLOT(error(QNetworkReply::NetworkError)));
QObject::connect(m_network, SIGNAL(finished()),
this, SLOT(finished()));
@@ -1389,13 +1423,17 @@ void QQmlXMLHttpRequest::finished()
QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirect.isValid()) {
QUrl url = m_network->url().resolved(redirect.toUrl());
- if (url.scheme() != QLatin1String("file")) {
+ if (!QQmlFile::isLocalFile(url)) {
// See http://www.ietf.org/rfc/rfc2616.txt, section 10.3.4 "303 See Other":
// Result of 303 redirection should be a new "GET" request.
const QVariant code = m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (code.isValid() && code.toInt() == 303 && m_method != QLatin1String("GET"))
m_method = QStringLiteral("GET");
destroyNetwork();
+
+ // Discard redirect response body
+ m_responseEntityBody = QByteArray();
+
requestFromUrl(url);
return;
}
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index f5e723419e..02628a9810 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -346,22 +346,22 @@ ReturnedValue QtObject::method_colorEqual(const FunctionObject *b, const Value *
bool ok = false;
QVariant lhs = scope.engine->toVariant(argv[0], -1);
- if (lhs.userType() == QVariant::String) {
+ if (lhs.userType() == QMetaType::QString) {
lhs = QQmlStringConverters::colorFromString(lhs.toString(), &ok);
if (!ok) {
THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid color name");
}
- } else if (lhs.userType() != QVariant::Color) {
+ } else if (lhs.userType() != QMetaType::QColor) {
THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments");
}
QVariant rhs = scope.engine->toVariant(argv[1], -1);
- if (rhs.userType() == QVariant::String) {
+ if (rhs.userType() == QMetaType::QString) {
rhs = QQmlStringConverters::colorFromString(rhs.toString(), &ok);
if (!ok) {
THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid color name");
}
- } else if (rhs.userType() != QVariant::Color) {
+ } else if (rhs.userType() != QMetaType::QColor) {
THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments");
}
@@ -612,13 +612,13 @@ ReturnedValue QtObject::method_lighter(const FunctionObject *b, const Value *, c
THROW_GENERIC_ERROR("Qt.lighter(): Invalid arguments");
QVariant v = scope.engine->toVariant(argv[0], -1);
- if (v.userType() == QVariant::String) {
+ if (v.userType() == QMetaType::QString) {
bool ok = false;
v = QQmlStringConverters::colorFromString(v.toString(), &ok);
if (!ok) {
return QV4::Encode::null();
}
- } else if (v.userType() != QVariant::Color) {
+ } else if (v.userType() != QMetaType::QColor) {
return QV4::Encode::null();
}
@@ -652,13 +652,13 @@ ReturnedValue QtObject::method_darker(const FunctionObject *b, const Value *, co
THROW_GENERIC_ERROR("Qt.darker(): Invalid arguments");
QVariant v = scope.engine->toVariant(argv[0], -1);
- if (v.userType() == QVariant::String) {
+ if (v.userType() == QMetaType::QString) {
bool ok = false;
v = QQmlStringConverters::colorFromString(v.toString(), &ok);
if (!ok) {
return QV4::Encode::null();
}
- } else if (v.userType() != QVariant::Color) {
+ } else if (v.userType() != QMetaType::QColor) {
return QV4::Encode::null();
}
@@ -703,143 +703,187 @@ ReturnedValue QtObject::method_tint(const FunctionObject *b, const Value *, cons
// base color
QVariant v1 = scope.engine->toVariant(argv[0], -1);
- if (v1.userType() == QVariant::String) {
+ if (v1.userType() == QMetaType::QString) {
bool ok = false;
v1 = QQmlStringConverters::colorFromString(v1.toString(), &ok);
if (!ok) {
return QV4::Encode::null();
}
- } else if (v1.userType() != QVariant::Color) {
+ } else if (v1.userType() != QMetaType::QColor) {
return QV4::Encode::null();
}
// tint color
QVariant v2 = scope.engine->toVariant(argv[1], -1);
- if (v2.userType() == QVariant::String) {
+ if (v2.userType() == QMetaType::QString) {
bool ok = false;
v2 = QQmlStringConverters::colorFromString(v2.toString(), &ok);
if (!ok) {
return QV4::Encode::null();
}
- } else if (v2.userType() != QVariant::Color) {
+ } else if (v2.userType() != QMetaType::QColor) {
return QV4::Encode::null();
}
return scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2));
}
+namespace {
+template <typename T>
+QString formatDateTimeObjectUsingDateFormat(T formatThis, Qt::DateFormat format) {
+ switch (format) {
+ case Qt::TextDate:
+ case Qt::ISODate:
+ case Qt::RFC2822Date:
+ case Qt::ISODateWithMs:
+ return formatThis.toString(format);
+ // ### Qt 6: Remove all locale dependent cases
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
+ case Qt::SystemLocaleDate:
+ // case Qt::LocalDate: covered by SystemLocaleDate
+ return QLocale::system().toString(formatThis);
+ case Qt::LocaleDate:
+ case Qt::DefaultLocaleShortDate:
+ return QLocale().toString(formatThis, QLocale::ShortFormat);
+ case Qt::SystemLocaleShortDate:
+ return QLocale::system().toString(formatThis, QLocale::ShortFormat);
+ case Qt::SystemLocaleLongDate:
+ return QLocale::system().toString(formatThis, QLocale::LongFormat);
+ case Qt::DefaultLocaleLongDate:
+ return QLocale().toString(formatThis, QLocale::LongFormat);
+ }
+ QT_WARNING_POP
+ Q_UNREACHABLE();
+ return QString();
+}
+
+template <typename T>
+ReturnedValue formatDateTimeObject(const T &formatThis, const QV4::Scope &scope, const QString &functionName, int argc, const Value *argv) {
+
+ QString formatted;
+ if (argc >= 2) {
+ QV4::ScopedString s(scope, argv[1]);
+ if (s) {
+ if (argc == 3)
+ scope.engine->throwError(QLatin1String("%1(): Stay argument, third argument can only be used if second argument is a locale").arg(functionName));
+ QString format = s->toQString();
+ formatted = formatThis.toString(format);
+ } else if (argv[1].isNumber()) {
+ if (argc == 3)
+ scope.engine->throwError(QLatin1String("%1(): Stay argument, third argument can only be used if second argument is a locale").arg(functionName));
+ quint32 intFormat = argv[1].asDouble();
+ Qt::DateFormat format = Qt::DateFormat(intFormat);
+ formatted = formatDateTimeObjectUsingDateFormat(formatThis, format);
+ } else {
+ QLocale::FormatType formatOptions = QLocale::ShortFormat;
+ if (argc == 3) {
+ if (argv[2].isNumber())
+ formatOptions = QLocale::FormatType(quint32(argv[2].asDouble()));
+ else
+ scope.engine->throwError(QLatin1String("%1(): Third argument must be a Locale format option").arg(functionName));
+ }
+ auto enginePriv = QQmlEnginePrivate::get(scope.engine->qmlEngine());
+ auto localeMetaTypeId = qMetaTypeId<QLocale>();
+ QVariant locale = enginePriv->v4engine()->toVariant(argv[1], localeMetaTypeId);
+ if (!locale.canConvert(localeMetaTypeId))
+ scope.engine->throwError(QLatin1String("%1(): Bad second argument (must be either string, number or locale)").arg(functionName));
+ formatted = locale.value<QLocale>().toString(formatThis, formatOptions);
+ }
+ } else {
+ formatted = QLocale().toString(formatThis, QLocale::ShortFormat);
+ }
+
+ return Encode(scope.engine->newString(formatted));
+}
+
+}
+
/*!
-\qmlmethod string Qt::formatDate(datetime date, variant format)
+\qmlmethod string Qt::formatDate(datetime date, variant format, variant localeFormatOption)
-Returns a string representation of \a date, optionally formatted according
-to \a format.
+Returns a string representation of \a date, optionally formatted using \a format.
The \a date parameter may be a JavaScript \c Date object, a \l{date}{date}
-property, a QDate, or QDateTime value. The \a format parameter may be any of
-the possible format values as described for
+property, a QDate, or QDateTime value. The \a format and \a localeFormatOption
+parameter may be any of the possible format values as described for
\l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}.
If \a format is not specified, \a date is formatted using
-\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}.
+\l {QLocale::FormatType}{Locale.ShortFormat} using the
+default locale.
\sa Locale
*/
ReturnedValue QtObject::method_formatDate(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
QV4::Scope scope(b);
- if (argc < 1 || argc > 2)
- THROW_GENERIC_ERROR("Qt.formatDate(): Invalid arguments");
+ if (argc < 1)
+ THROW_GENERIC_ERROR("Qt.formatDate(): Missing argument");
+ if (argc > 3)
+ THROW_GENERIC_ERROR("Qt.formatDate(): Stray arguments; formatDate takes at most 3 arguments.");
- Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate;
QDate date = scope.engine->toVariant(argv[0], -1).toDateTime().date();
- QString formattedDate;
- if (argc == 2) {
- QV4::ScopedString s(scope, argv[1]);
- if (s) {
- QString format = s->toQString();
- formattedDate = date.toString(format);
- } else if (argv[1].isNumber()) {
- quint32 intFormat = argv[1].asDouble();
- Qt::DateFormat format = Qt::DateFormat(intFormat);
- formattedDate = date.toString(format);
- } else {
- THROW_GENERIC_ERROR("Qt.formatDate(): Invalid date format");
- }
- } else {
- formattedDate = date.toString(enumFormat);
- }
-
- return Encode(scope.engine->newString(formattedDate));
+ return formatDateTimeObject(date, scope, QLatin1String("Qt.formatDate"), argc, argv);
}
/*!
-\qmlmethod string Qt::formatTime(datetime time, variant format)
+\qmlmethod string Qt::formatTime(datetime time, variant format, variant localeFormatOption)
-Returns a string representation of \a time, optionally formatted according to
-\a format.
+Returns a string representation of \a time, optionally formatted using
+\a format, and, if provided, \a localeFormatOption.
The \a time parameter may be a JavaScript \c Date object, a QTime, or QDateTime
-value. The \a format parameter may be any of the possible format values as
-described for \l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}.
+value. The \a format and \a localeFormatOption parameter may be any of the
+possible format values as described for
+\l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}.
If \a format is not specified, \a time is formatted using
-\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}.
+\l {QLocale::FormatType}{Locale.ShortFormat} using the default locale.
\sa Locale
*/
ReturnedValue QtObject::method_formatTime(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
QV4::Scope scope(b);
- if (argc < 1 || argc > 2)
- THROW_GENERIC_ERROR("Qt.formatTime(): Invalid arguments");
+ if (argc < 1)
+ THROW_GENERIC_ERROR("Qt.formatTime(): Missing argument");
+ if (argc > 3)
+ THROW_GENERIC_ERROR("Qt.formatTime(): Stray arguments; formatTime takes at most 3 arguments.");
QVariant argVariant = scope.engine->toVariant(argv[0], -1);
QTime time;
- if (argv[0].as<DateObject>() || (argVariant.type() == QVariant::String))
+ if (argv[0].as<DateObject>() || (argVariant.userType() == QMetaType::QString))
time = argVariant.toDateTime().time();
else // if (argVariant.type() == QVariant::Time), or invalid.
time = argVariant.toTime();
-
- Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate;
- QString formattedTime;
- if (argc == 2) {
- QV4::ScopedString s(scope, argv[1]);
- if (s) {
- QString format = s->toQString();
- formattedTime = time.toString(format);
- } else if (argv[1].isNumber()) {
- quint32 intFormat = argv[1].asDouble();
- Qt::DateFormat format = Qt::DateFormat(intFormat);
- formattedTime = time.toString(format);
- } else {
- THROW_GENERIC_ERROR("Qt.formatTime(): Invalid time format");
- }
- } else {
- formattedTime = time.toString(enumFormat);
- }
-
- return Encode(scope.engine->newString(formattedTime));
+ return formatDateTimeObject(time, scope, QLatin1String("Qt.formatTime"), argc, argv);
}
/*!
-\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format)
+\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format, variant localeFormatOption)
-Returns a string representation of \a dateTime, optionally formatted according to
-\a format.
+Returns a string representation of \a dateTime, optionally formatted using
+\a format and \a localeFormatOption.
The \a dateTime parameter may be a JavaScript \c Date object, a \l{date}{date}
property, a QDate, QTime, or QDateTime value.
If \a format is not provided, \a dateTime is formatted using
-\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. Otherwise,
-\a format should be either:
+\l {QLocale::FormatType}{Locale.ShortFormat} using the
+default locale. Otherwise, \a format should be either:
\list
\li One of the Qt::DateFormat enumeration values, such as
- \c Qt.DefaultLocaleShortDate or \c Qt.ISODate
+ \c Qt.RFC2822Date or \c Qt.ISODate.
\li A string that specifies the format of the returned string, as detailed below.
+\li A \c locale object.
\endlist
+If \a format specifies a locale object, \dateTime is formatted
+with \l{QLocale::toString}. In this case, \a localeFormatOption can hold a value
+of type \l {QLocale::FormatType} to further tune the formatting. If none is
+provided, \l {QLocale::FormatType}{Locale.ShortFormat} is used.
+
If \a format specifies a format string, it should use the following expressions
to specify the date:
@@ -916,29 +960,13 @@ with the \a format values below to produce the following results:
ReturnedValue QtObject::method_formatDateTime(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
QV4::Scope scope(b);
- if (argc < 1 || argc > 2)
- THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid arguments");
+ if (argc < 1)
+ THROW_GENERIC_ERROR("Qt.formatDateTime(): Missing argument");
+ if (argc > 3)
+ THROW_GENERIC_ERROR("Qt.formatDateTime(): Stray arguments; formatDate takes at most 3 arguments.");
- Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate;
QDateTime dt = scope.engine->toVariant(argv[0], -1).toDateTime();
- QString formattedDt;
- if (argc == 2) {
- QV4::ScopedString s(scope, argv[1]);
- if (s) {
- QString format = s->toQString();
- formattedDt = dt.toString(format);
- } else if (argv[1].isNumber()) {
- quint32 intFormat = argv[1].asDouble();
- Qt::DateFormat format = Qt::DateFormat(intFormat);
- formattedDt = dt.toString(format);
- } else {
- THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid datetime format");
- }
- } else {
- formattedDt = dt.toString(enumFormat);
- }
-
- return Encode(scope.engine->newString(formattedDt));
+ return formatDateTimeObject(dt, scope, QLatin1String("Qt.formatDateTime"), argc, argv);
}
/*!
@@ -1338,6 +1366,41 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va
return QV4::QObjectWrapper::wrap(scope.engine, c);
}
+ReturnedValue QtObject::method_get_uiLanguage(const FunctionObject *b, const Value * /*thisObject*/, const Value * /*argv*/, int /*argc*/)
+{
+ QV4::Scope scope(b);
+ QJSEngine *jsEngine = scope.engine->jsEngine();
+ if (!jsEngine)
+ return Encode::null();
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(scope.engine);
+ if (ep && ep->propertyCapture) {
+ static int propertyIndex = -1;
+ static int notifySignalIndex = -1;
+ if (propertyIndex < 0) {
+ QMetaProperty metaProperty =
+ QQmlEngine::staticMetaObject.property(QQmlEngine::staticMetaObject.indexOfProperty("uiLanguage"));
+ propertyIndex = metaProperty.propertyIndex();
+ notifySignalIndex = metaProperty.notifySignalIndex();
+ }
+ ep->propertyCapture->captureProperty(QQmlEnginePrivate::get(ep), propertyIndex, notifySignalIndex);
+ }
+
+ return Encode(scope.engine->newString(QJSEnginePrivate::get(jsEngine)->uiLanguage));
+}
+
+ReturnedValue QtObject::method_set_uiLanguage(const FunctionObject *b, const Value * /*thisObject*/, const Value *argv, int argc)
+{
+ Scope scope(b);
+ if (!argc)
+ THROW_TYPE_ERROR();
+ QJSEngine *jsEngine = scope.engine->jsEngine();
+ if (!jsEngine)
+ THROW_TYPE_ERROR();
+ jsEngine->setUiLanguage(argv[0].toQString());
+ return Encode::undefined();
+}
+
#if QT_CONFIG(qml_locale)
/*!
\qmlmethod Qt::locale(name)
@@ -1835,6 +1898,14 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext
globalObject->defineDefaultProperty(QStringLiteral("qsTrId"), QV4::GlobalExtensions::method_qsTrId);
globalObject->defineDefaultProperty(QStringLiteral("QT_TRID_NOOP"), QV4::GlobalExtensions::method_qsTrIdNoOp);
+ ScopedString qtName(scope, v4->newString(QStringLiteral("Qt")));
+ ScopedObject qt(scope, globalObject->get(qtName));
+ if (!qt) {
+ qt = v4->newObject();
+ globalObject->defineDefaultProperty(qtName, qt);
+ }
+ qt->defineAccessorProperty(QStringLiteral("uiLanguage"), QV4::QtObject::method_get_uiLanguage, QV4::QtObject::method_set_uiLanguage);
+
// string prototype extension
scope.engine->stringPrototype()->defineDefaultProperty(QStringLiteral("arg"), QV4::GlobalExtensions::method_string_arg);
#endif
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index d87b83ba10..5cbb52471d 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -126,6 +126,8 @@ struct QtObject : Object
static ReturnedValue method_resolvedUrl(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_createQmlObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_createComponent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_uiLanguage(const FunctionObject *b, const Value *, const Value *, int);
+ static ReturnedValue method_set_uiLanguage(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
#if QT_CONFIG(qml_locale)
static ReturnedValue method_locale(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
#endif