diff options
Diffstat (limited to 'src/qml/qml')
-rw-r--r-- | src/qml/qml/qqml.cpp | 23 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmlcontext.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine_p.h | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlextensionplugin.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlfile.cpp | 185 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 59 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype_p.h | 7 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertybinding.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache.cpp | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycachecreator.cpp | 23 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycachecreator_p.h | 16 | ||||
-rw-r--r-- | src/qml/qml/qqmlproxymetaobject.cpp | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmltype.cpp | 9 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 25 |
16 files changed, 265 insertions, 122 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 8781e4bf50..b9c4865336 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -223,6 +223,7 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) /*! * \enum QQmlModuleImportSpecialVersions + * \relates QQmlEngine * * Defines some special values that can be passed to the version arguments of * qmlRegisterModuleImport() and qmlUnregisterModuleImport(). @@ -241,25 +242,26 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) */ /*! - * Registers an implicit import for module \a uri of major version \a majorVersion. + * \relates QQmlEngine + * Registers an implicit import for module \a uri of major version \a moduleMajor. * * This has the same effect as an \c import statement in a qmldir file: Whenever * \a uri of version \a moduleMajor is imported, \a import of version * \a importMajor. \a importMinor is automatically imported, too. If - * \a importMajor is \l QmlModuleImportLatest the latest version + * \a importMajor is \l QQmlModuleImportLatest the latest version * available of that module is imported, and \a importMinor does not matter. If - * \a importMinor is \l QmlModuleImportLatest the latest minor version of a - * \a importMajor is chosen. If \a importMajor is \l QmlModuleImportAuto the + * \a importMinor is \l QQmlModuleImportLatest the latest minor version of a + * \a importMajor is chosen. If \a importMajor is \l QQmlModuleImportAuto the * version of \a import is version of \a uri being imported, and \a importMinor - * does not matter. If \a moduleMajor is \a QmlModuleImportModuleAny the module + * does not matter. If \a moduleMajor is \l QQmlModuleImportModuleAny the module * import is applied for any major version of \a uri. For example, you may * specify that whenever any version of MyModule is imported, the latest version * of MyOtherModule should be imported. Then, the following call would be * appropriate: * * \code - * qmlRegisterModuleImport("MyModule", QmlModuleImportModuleAny, - * "MyOtherModule", QmlModuleImportLatest); + * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny, + * "MyOtherModule", QQmlModuleImportLatest); * \endcode * * Or, you may specify that whenever major version 5 of "MyModule" is imported, @@ -273,8 +275,8 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) * imported whenever "MyModule" is imported, specify the following: * * \code - * qmlRegisterModuleImport("MyModule", QmlModuleImportModuleAny, - * "MyOtherModule", QmlModuleImportAuto); + * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny, + * "MyOtherModule", QQmlModuleImportAuto); * \endcode * * \sa qmlUnregisterModuleImport() @@ -289,6 +291,7 @@ void qmlRegisterModuleImport(const char *uri, int moduleMajor, /*! + * \relates QQmlEngine * Removes a module import previously registered with qmlRegisterModuleImport() * * Calling this function makes sure that \a import of version @@ -506,7 +509,7 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data) const QTypeRevision added = revisionClassInfo( type.classInfoMetaObject, "QML.AddedInVersion", - QTypeRevision::fromMinorVersion(0)); + QTypeRevision::fromVersion(type.version.majorVersion(), 0)); const QTypeRevision removed = revisionClassInfo( type.classInfoMetaObject, "QML.RemovedInVersion"); const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject, diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index addee70b6b..f54eee3e72 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -862,8 +862,16 @@ QObject *QQmlComponent::create(QQmlContext *context) Q_D(QQmlComponent); QObject *rv = d->doBeginCreate(this, context); - if (rv) + if (rv) { completeCreate(); + } else if (d->state.completePending) { + // overridden completCreate might assume that + // the object has actually been created + ++creationDepth.localData(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->engine); + d->complete(ep, &d->state); + --creationDepth.localData(); + } if (rv && !d->requiredProperties().empty()) { delete rv; return nullptr; diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h index 82d15b8102..2da9541e5a 100644 --- a/src/qml/qml/qqmlcontext.h +++ b/src/qml/qml/qqmlcontext.h @@ -108,6 +108,4 @@ private: }; QT_END_NAMESPACE -Q_DECLARE_METATYPE(QList<QObject*>) - #endif // QQMLCONTEXT_H diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e3840c8011..9963e4d957 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -163,7 +163,7 @@ QT_BEGIN_NAMESPACE \endcode */ -bool QQmlEnginePrivate::qml_debugging_enabled = false; +std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false}; bool QQmlEnginePrivate::s_designerMode = false; bool QQmlEnginePrivate::designerMode() diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 630d32736b..97c2f95450 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -80,6 +80,8 @@ #include <QtCore/qstring.h> #include <QtCore/qthread.h> +#include <atomic> + QT_BEGIN_NAMESPACE class QNetworkAccessManager; @@ -250,7 +252,7 @@ public: static bool designerMode(); static void activateDesignerMode(); - static bool qml_debugging_enabled; + static std::atomic<bool> qml_debugging_enabled; mutable QMutex networkAccessManagerMutex; diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp index ecac16102d..3a73a5eae1 100644 --- a/src/qml/qml/qqmlextensionplugin.cpp +++ b/src/qml/qml/qqmlextensionplugin.cpp @@ -184,7 +184,8 @@ void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char /*! \macro Q_IMPORT_QML_PLUGIN(PluginName) - \relates <QQmlExtensionPlugin> + \since 6.2 + \relates QQmlEngineExtensionPlugin Ensures the plugin whose metadata-declaring class is named \a PluginName is linked into static builds. diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index 4db8981975..181efe49aa 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -65,6 +65,9 @@ static char file_string[] = "file"; #if defined(Q_OS_ANDROID) static char assets_string[] = "assets"; static char content_string[] = "content"; +static char authority_externalstorage[] = "com.android.externalstorage.documents"; +static char authority_downloads_documents[] = "com.android.providers.downloads.documents"; +static char authority_media_documents[] = "com.android.providers.media.documents"; #endif class QQmlFilePrivate; @@ -505,6 +508,17 @@ bool QQmlFile::isSynchronous(const QString &url) return false; } +#if defined(Q_OS_ANDROID) +static bool hasLocalContentAuthority(const QUrl &url) +{ + const QString authority = url.authority(); + return authority.isEmpty() + || authority == QLatin1String(authority_externalstorage) + || authority == QLatin1String(authority_downloads_documents) + || authority == QLatin1String(authority_media_documents); +} +#endif + /*! Returns true if \a url is a local file that can be opened with QFile. @@ -516,58 +530,116 @@ bool QQmlFile::isLocalFile(const QUrl &url) { QString scheme = url.scheme(); - if ((scheme.length() == 4 && 0 == scheme.compare(QLatin1String(file_string), Qt::CaseInsensitive)) || - (scheme.length() == 3 && 0 == scheme.compare(QLatin1String(qrc_string), Qt::CaseInsensitive))) { + // file: URLs with two slashes following the scheme can be interpreted as local files + // where the slashes are part of the path. Therefore, disregard the authority. + // See QUrl::toLocalFile(). + if (scheme.length() == 4 && scheme.startsWith(QLatin1String(file_string), Qt::CaseInsensitive)) return true; + if (scheme.length() == 3 && scheme.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive)) + return url.authority().isEmpty(); + #if defined(Q_OS_ANDROID) - } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) { - return true; + if (scheme.length() == 6 + && scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive)) + return url.authority().isEmpty(); + if (scheme.length() == 7 + && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive)) + return hasLocalContentAuthority(url); #endif - } else { + return false; +} + +static bool hasScheme(const QString &url, const char *scheme, qsizetype schemeLength) +{ + const qsizetype urlLength = url.length(); + + if (urlLength < schemeLength + 1) + return false; + + if (!url.startsWith(QLatin1String(scheme, scheme + schemeLength), Qt::CaseInsensitive)) + return false; + + if (url[schemeLength] != QLatin1Char(':')) return false; + + return true; +} + +static qsizetype authorityOffset(const QString &url, qsizetype schemeLength) +{ + const qsizetype urlLength = url.length(); + + if (urlLength < schemeLength + 3) + return -1; + + const QLatin1Char slash('/'); + if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) { + // Exactly two slashes denote an authority. + if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash) + return schemeLength + 3; } + + return -1; } +#if defined(Q_OS_ANDROID) +static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength) +{ + const qsizetype offset = authorityOffset(url, schemeLength); + if (offset == -1) + return true; // no authority is a local authority. + + const QString authorityAndPath = url.sliced(offset); + return authorityAndPath.startsWith(QLatin1String(authority_externalstorage)) + || authorityAndPath.startsWith(QLatin1String(authority_downloads_documents)) + || authorityAndPath.startsWith(QLatin1String(authority_media_documents)); +} + +#endif + /*! Returns true if \a url is a local file that can be opened with QFile. -Local file urls have either a qrc:/ or file:// scheme. +Local file urls have either a qrc: or file: scheme. -\note On Android, urls with assets:/ scheme are also considered local files. +\note On Android, urls with assets: or content: scheme are also considered local files. */ bool QQmlFile::isLocalFile(const QString &url) { - if (url.length() < 5 /* qrc:/ */) + if (url.length() < 4 /* qrc: */) return false; - QChar f = url[0]; - - if (f == QLatin1Char('f') || f == QLatin1Char('F')) { - - return url.length() >= 7 /* file:// */ && - url.startsWith(QLatin1String(file_string), Qt::CaseInsensitive) && - url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/'); - - } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) { - - return url.length() >= 5 /* qrc:/ */ && - url.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive) && - url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/'); - + switch (url[0].toLatin1()) { + case 'f': + case 'F': { + // file: URLs with two slashes following the scheme can be interpreted as local files + // where the slashes are part of the path. Therefore, disregard the authority. + // See QUrl::toLocalFile(). + const qsizetype fileLength = strlen(file_string); + return url.startsWith(QLatin1String(file_string, file_string + fileLength), + Qt::CaseInsensitive) + && url.length() > fileLength + && url[fileLength] == QLatin1Char(':'); } + case 'q': + case 'Q': + return hasScheme(url, qrc_string, strlen(qrc_string)) + && authorityOffset(url, strlen(qrc_string)) == -1; #if defined(Q_OS_ANDROID) - else if (f == QLatin1Char('a') || f == QLatin1Char('A')) { - return url.length() >= 8 /* assets:/ */ && - url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) && - url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/'); - } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) { - return url.length() >= 9 /* content:/ */ && - url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) && - url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); - } + case 'a': + case 'A': + return hasScheme(url, assets_string, strlen(assets_string)) + && authorityOffset(url, strlen(assets_string)) == -1; + case 'c': + case 'C': + return hasScheme(url, content_string, strlen(content_string)) + && hasLocalContentAuthority(url, strlen(content_string)); #endif + default: + break; + } return false; } @@ -585,15 +657,14 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url) } #if defined(Q_OS_ANDROID) - else if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0) { - if (url.authority().isEmpty()) + if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0) + return url.authority().isEmpty() ? url.toString() : QString(); + if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) { + if (hasLocalContentAuthority(url)) return url.toString(); return QString(); - } else if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) { - return url.toString(); } #endif - return url.toLocalFile(); } @@ -603,11 +674,28 @@ static QString toLocalFile(const QString &url) if (!file.isLocalFile()) return QString(); - //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt" + // QUrl::toLocalFile() interprets two slashes as part of the path. + // Therefore windows hostnames like "//servername/path/to/file.txt" are preserved. return file.toLocalFile(); } +static bool isDoubleSlashed(const QString &url, qsizetype offset) +{ + const qsizetype urlLength = url.length(); + if (urlLength < offset + 2) + return false; + + const QLatin1Char slash('/'); + if (url[offset] != slash || url[offset + 1] != slash) + return false; + + if (urlLength < offset + 3) + return true; + + return url[offset + 2] != slash; +} + /*! If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an empty string. @@ -615,23 +703,28 @@ empty string. QString QQmlFile::urlToLocalFileOrQrc(const QString& url) { if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) { - if (url.length() > 6) - return QLatin1Char(':') + QStringView{url}.mid(6); - return QString(); + // Exactly two slashes are bad because that's a URL authority. + // One slash is fine and >= 3 slashes are file. + if (url.length() == 6 || url[6] != QLatin1Char('/')) { + Q_ASSERT(isDoubleSlashed(url, strlen("qrc:"))); + return QString(); + } + Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:"))); + return QLatin1Char(':') + QStringView{url}.mid(6); } if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) { + Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:"))); if (url.length() > 4) return QLatin1Char(':') + QStringView{url}.mid(4); - return QString(); + return QStringLiteral(":"); } #if defined(Q_OS_ANDROID) - else if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive)) { - return url; - } else if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive)) { - return url; - } + if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive)) + return isDoubleSlashed(url, strlen("assets:")) ? QString() : url; + if (hasScheme(url, content_string, strlen(content_string))) + return hasLocalContentAuthority(url, strlen(content_string)) ? url : QString(); #endif return toLocalFile(url); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 610a39a992..9feb7a5dfc 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -236,7 +236,8 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el } void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, - const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd, + QQmlMetaType::ClonePolicy policy) { // Set classname builder.setClassName(ignoreEnd->className()); @@ -253,40 +254,41 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } - // Clone Q_METHODS - do this first to avoid duplicating the notify signals. - for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { - QMetaMethod method = mo->method(ii); + if (policy != QQmlMetaType::CloneEnumsOnly) { + // Clone Q_METHODS - do this first to avoid duplicating the notify signals. + for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { + QMetaMethod method = mo->method(ii); - // More complex - need to search name - QByteArray name = method.name(); + // More complex - need to search name + QByteArray name = method.name(); + bool found = false; - bool found = false; + for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); + !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) { - for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); - !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); - ++ii) { + QMetaMethod other = ignoreEnd->method(ii); - QMetaMethod other = ignoreEnd->method(ii); + found = name == other.name(); + } - found = name == other.name(); + QMetaMethodBuilder m = builder.addMethod(method); + if (found) // SKIP + m.setAccess(QMetaMethod::Private); } - QMetaMethodBuilder m = builder.addMethod(method); - if (found) // SKIP - m.setAccess(QMetaMethod::Private); - } - - // Clone Q_PROPERTY - for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { - QMetaProperty property = mo->property(ii); + // Clone Q_PROPERTY + for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { + QMetaProperty property = mo->property(ii); - int otherIndex = ignoreEnd->indexOfProperty(property.name()); - if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { - builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void")); - // Skip - } else { - builder.addProperty(property); + int otherIndex = ignoreEnd->indexOfProperty(property.name()); + if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { + builder.addProperty(QByteArray("__qml_ignore__") + property.name(), + QByteArray("void")); + // Skip + } else { + builder.addProperty(property); + } } } @@ -851,6 +853,8 @@ QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes( QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); } +#else + Q_UNUSED(basePath) #endif const QByteArray bytes = uri.toUtf8(); @@ -1519,7 +1523,8 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject return; QMetaObjectBuilder builder; - clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject); + clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject, + extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = baseMetaObject; if (!metaObjects.isEmpty()) diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 2b00b75dc6..7777562454 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -243,8 +243,13 @@ public: const QMetaObject *baseMetaObject, QMetaObject *lastMetaObject); + enum ClonePolicy { + CloneAll, // default + CloneEnumsOnly, // skip properties and methods + }; static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, - const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd); + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd, + ClonePolicy policy); static void qmlInsertModuleRegistration(const QString &uri, void (*registerFunction)()); static void qmlRemoveModuleRegistration(const QString &uri); diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp index 7243615e3e..a73396a892 100644 --- a/src/qml/qml/qqmlpropertybinding.cpp +++ b/src/qml/qml/qqmlpropertybinding.cpp @@ -167,7 +167,8 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP void QQmlPropertyBindingJS::expressionChanged() { - if (!asBinding()->propertyDataPtr) + auto binding = asBinding(); + if (!binding->propertyDataPtr) return; const auto currentTag = m_error.tag(); if (currentTag == InEvaluationLoop) { @@ -187,8 +188,9 @@ void QQmlPropertyBindingJS::expressionChanged() return; } m_error.setTag(InEvaluationLoop); - asBinding()->evaluateRecursive(); - asBinding()->notifyRecursive(); + PendingBindingObserverList bindingObservers; + binding->evaluateRecursive(bindingObservers); + binding->notifyNonRecursive(bindingObservers); m_error.setTag(NoTag); } diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 5b44ead713..278977fb9c 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1038,6 +1038,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) const property.setWritable(data->isWritable()); property.setResettable(data->isResettable()); property.setBindable(data->isBindable()); + property.setAlias(data->isAlias()); } for (int ii = 0; ii < methods.count(); ++ii) { diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp index 45edb59cfb..fe68337eb3 100644 --- a/src/qml/qml/qqmlpropertycachecreator.cpp +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -115,8 +115,10 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() return true; } + if (!referencingObjectPropertyCache) + return false; + Q_ASSERT(referencingObjectIndex >= 0); - Q_ASSERT(referencingObjectPropertyCache); Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); bool notInRevision = false; @@ -149,15 +151,22 @@ void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePr if (propertyCaches->at(groupPropertyObjectIndex)) continue; - if (pendingBinding.referencingObjectPropertyCache) { - if (!pendingBinding.resolveInstantiatingProperty()) - continue; - auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); - propertyCaches->set(groupPropertyObjectIndex, cache); - } else { + if (pendingBinding.instantiatingPropertyName.isEmpty()) { + // Generalized group property. auto cache = propertyCaches->at(pendingBinding.referencingObjectIndex); propertyCaches->set(groupPropertyObjectIndex, cache); + continue; } + + if (!pendingBinding.referencingObjectPropertyCache) { + pendingBinding.referencingObjectPropertyCache + = propertyCaches->at(pendingBinding.referencingObjectIndex); + } + + if (!pendingBinding.resolveInstantiatingProperty()) + continue; + auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); + propertyCaches->set(groupPropertyObjectIndex, cache); } } diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 606628cbe8..09d8ace425 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -348,12 +348,8 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur switch (binding->type()) { case QV4::CompiledData::Binding::Type_Object: case QV4::CompiledData::Binding::Type_GroupProperty: - // We can resolve object and group properties if we have a property cache. - if (thisCache) - break; - continue; case QV4::CompiledData::Binding::Type_AttachedProperty: - // We can always resolve attached properties. + // We can always resolve object, group, and attached properties. break; default: // Everything else is of no interest here. @@ -949,10 +945,16 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor } const auto referencedType = typeRef->type(); - if (referencedType.isValid()) + if (referencedType.isValid()) { *type = referencedType.typeId(); - else + if (!type->isValid() && referencedType.isInlineComponentType()) { + int objectId = referencedType.inlineComponentId(); + *type = objectContainer->typeIdsForComponent(objectId).id; + Q_ASSERT(type->isValid()); + } + } else { *type = typeRef->compilationUnit()->typeIds.id; + } *version = typeRef->version(); diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp index 33b216ee78..a91652e9a1 100644 --- a/src/qml/qml/qqmlproxymetaobject.cpp +++ b/src/qml/qml/qqmlproxymetaobject.cpp @@ -68,11 +68,8 @@ QQmlProxyMetaObject::~QQmlProxyMetaObject() QObject *QQmlProxyMetaObject::getProxy(int index) { if (!proxies) { - if (!proxies) { - proxies = new QObject*[metaObjects->count()]; - ::memset(proxies, 0, - sizeof(QObject *) * metaObjects->count()); - } + proxies = new QObject *[metaObjects->count()]; + ::memset(proxies, 0, sizeof(QObject *) * metaObjects->count()); } if (!proxies[index]) { @@ -112,6 +109,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void const int globalPropertyOffset = metaObjects->at(ii).propertyOffset; if (id >= globalPropertyOffset) { QObject *proxy = getProxy(ii); + Q_ASSERT(proxy); const int localProxyOffset = proxy->metaObject()->propertyOffset(); const int localProxyId = id - globalPropertyOffset + localProxyOffset; @@ -129,7 +127,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void const int globalMethodOffset = metaObjects->at(ii).methodOffset; if (id >= globalMethodOffset) { QObject *proxy = getProxy(ii); - + Q_ASSERT(proxy); const int localMethodOffset = proxy->metaObject()->methodOffset(); const int localMethodId = id - globalMethodOffset + localMethodOffset; diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index f827a24660..748283ad03 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -219,16 +219,15 @@ void QQmlTypePrivate::init() const return; } - auto setupExtendedMetaObject = [&]( - const QMetaObject *extMetaObject, - QObject *(*extFunc)(QObject *)) { - + auto setupExtendedMetaObject = [&](const QMetaObject *extMetaObject, + QObject *(*extFunc)(QObject *)) { if (!extMetaObject) return; // XXX - very inefficient QMetaObjectBuilder builder; - QQmlMetaType::clone(builder, extMetaObject, extMetaObject, extMetaObject); + QQmlMetaType::clone(builder, extMetaObject, extMetaObject, extMetaObject, + extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = mo; QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 }; diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index c2bbd7f498..2cba0375e7 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -281,10 +281,27 @@ PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, { if (id.isString()) { const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); - QQmlPropertyData result = r->dataForPropertyKey(id); - if (p && result.isValid()) - p->value = getGadgetProperty(r->engine(), r->d(), result.propType(), result.coreIndex(), result.isFunction(), result.isEnum()); - return result.isValid() ? Attr_Data : Attr_Invalid; + Q_ASSERT(r); + + const QQmlPropertyData result = r->dataForPropertyKey(id); + if (!result.isValid()) + return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it. + + if (!p) + return Attr_Data; // Property exists, but we're not interested in the value + + const QQmlValueTypeReference *ref = r->as<const QQmlValueTypeReference>(); + if (!ref || ref->readReferenceValue()) { + // Property exists, and we can retrieve it + p->value = getGadgetProperty( + r->engine(), r->d(), result.propType(), result.coreIndex(), + result.isFunction(), result.isEnum()); + } else { + // Property exists, but we can't retrieve it. Make it undefined. + p->value = Encode::undefined(); + } + + return Attr_Data; } return QV4::Object::virtualGetOwnProperty(m, id, p); |