diff options
Diffstat (limited to 'src/qml/qml')
27 files changed, 587 insertions, 215 deletions
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index a93b012c70..6295345fa4 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -102,7 +102,7 @@ class QQmlPropertyValueInterceptor; void Q_QML_EXPORT qmlClearTypeRegistrations(); template<typename T> -int qmlRegisterAnonymousType(const char *uri, int versionMajor=1) +int qmlRegisterAnonymousType(const char *uri, int versionMajor) { QML_GETTYPENAMES @@ -136,7 +136,7 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor=1) template<typename T> QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegisterType() { - return qmlRegisterAnonymousType<T>(""); + return qmlRegisterAnonymousType<T>("", 1); } int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 3a437eab8d..2f6aabf61e 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -315,7 +315,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()); } } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index ed8c41a582..d651cbf636 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -340,6 +340,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,8 +374,8 @@ 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); + QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(component, name, requiredProperties()); + QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop); if (!prop.isValid() || !privProp->writeValueProperty(value, nullptr)) { QQmlError error{}; error.setUrl(url); @@ -809,6 +819,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; } @@ -828,6 +842,10 @@ QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialPr setInitialProperties(rv, initialProperties); completeCreate(); } + if (!d->requiredProperties().empty()) { + d->requiredProperties().clear(); + return nullptr; + } return rv; } @@ -980,6 +998,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. @@ -998,6 +1067,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); @@ -1185,7 +1259,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); }; } @@ -1210,7 +1284,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 @@ -1282,7 +1357,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); @@ -1301,6 +1376,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); @@ -1317,12 +1393,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(unsetRequiredProperty.location.line); + error.setColumn(unsetRequiredProperty.location.column); + return error; +} + /*! \internal */ @@ -1370,7 +1474,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(); @@ -1502,7 +1616,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); @@ -1511,7 +1625,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) @@ -1601,7 +1715,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); @@ -1610,7 +1724,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_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/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 136159993a..96891af416 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -98,11 +98,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ @@ -137,11 +137,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index bc06226cbf..5c3ecbfb60 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) { @@ -296,6 +297,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 +327,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()) @@ -657,6 +678,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 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..f075407e73 100644 --- a/src/qml/qml/qqmlincubator.h +++ b/src/qml/qml/qqmlincubator.h @@ -47,6 +47,9 @@ QT_BEGIN_NAMESPACE class QQmlEngine; +class QQmlPropertyData; +class QVariant; +using QVariantMap = QMap<QString, QVariant>; class QQmlIncubatorPrivate; class Q_QML_EXPORT QQmlIncubator @@ -84,6 +87,8 @@ public: QObject *object() const; + void setInitialProperties(const QVariantMap &initialProperties); + protected: virtual void statusChanged(Status); virtual void setInitialState(QObject *); diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index 57ec8249cb..aadb147bd5 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -61,8 +61,12 @@ QT_BEGIN_NAMESPACE +class QQmlPropertyData; +struct RequiredPropertyInfo; +using RequiredProperties = QHash<QQmlPropertyData*, RequiredPropertyInfo>; + 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 +101,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/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 2c641d3845..c2674b402a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -739,9 +739,16 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString, const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString)); QQmlMetaTypeDataPtr data; - QQmlType ret(data->urlToType.value(url)); - if (ret.isValid() && ret.sourceUrl() == url) - return ret; + { + QQmlType ret(data->urlToType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } + { + QQmlType ret(data->urlToNonFileImportType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } const int dot = qualifiedType.indexOf(QLatin1Char('.')); const QString typeName = dot < 0 diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index f89608cd5d..d2e9d36d55 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -82,6 +82,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR , propertyCaches(&compilationUnit->propertyCaches) , sharedState(new QQmlObjectCreatorSharedState) , topLevelCreator(true) + , hadRequiredProperties(false) , incubator(incubator) { init(parentContext); @@ -783,6 +784,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; @@ -794,8 +812,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 }; @@ -1504,10 +1520,43 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) _ddata->deferData(_compiledObjectIndex, compilationUnit, context); + for (int propertyIndex = 0; propertyIndex != _compiledObject->propertyCount(); ++propertyIndex) { + const QV4::CompiledData::Property* property = _compiledObject->propertiesBegin() + propertyIndex; + QQmlPropertyData *propertyData = _propertyCache->property(_propertyCache->propertyOffset() + propertyIndex); + if (property->isRequired) { + hadRequiredProperties = true; + sharedState->requiredProperties.insert(propertyData, + RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex), compilationUnit->finalUrl(), property->location, {}}); + } + } + 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..c302660799 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; +}; + +using RequiredProperties = QHash<QQmlPropertyData*, RequiredPropertyInfo>; + struct QQmlObjectCreatorSharedState : public QSharedData { QQmlContextData *rootContext; @@ -78,6 +100,7 @@ struct QQmlObjectCreatorSharedState : public QSharedData QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; QQmlVmeProfiler profiler; QRecursionNode recursionNode; + RequiredProperties requiredProperties; }; class Q_QML_PRIVATE_EXPORT QQmlObjectCreator @@ -102,6 +125,9 @@ public: QQmlContextData *parentContextData() const { return parentContext.contextData(); } QFiniteStack<QPointer<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; } + RequiredProperties &requiredProperties() {return sharedState->requiredProperties;} + bool componentHadRequiredProperties() const {return hadRequiredProperties;} + private: QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); @@ -147,6 +173,7 @@ private: const QQmlPropertyCacheVector *propertyCaches; QExplicitlySharedDataPointer<QQmlObjectCreatorSharedState> sharedState; bool topLevelCreator; + bool hadRequiredProperties; QQmlIncubatorPrivate *incubator; QObject *_qobject; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 5f57e0eca1..eff3e94fbd 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -885,7 +885,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 diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 9c7a69d571..def4480198 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -256,17 +256,8 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine Q_ASSERT(typeRef); QQmlType qmltype = typeRef->type; if (!qmltype.isValid()) { - QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) { - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } + imports->resolveType(stringAt(context.instantiatingBinding->propertyNameIndex), + &qmltype, nullptr, nullptr, nullptr); } const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 7dbcbe986b..6959b05105 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -49,6 +49,19 @@ QT_BEGIN_NAMESPACE +static bool isPrimitiveType(int typeId) +{ + switch (typeId) { +#define HANDLE_PRIMITIVE(Type, id, T) \ + case QMetaType::Type: +QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE); +#undef HANDLE_PRIMITIVE + return true; + default: + return false; + } +} + QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) : enginePrivate(enginePrivate) , compilationUnit(compilationUnit) @@ -281,11 +294,21 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( return recordError(binding->location, tr("Invalid grouped property access")); } } else { - if (!enginePrivate->propertyCacheForType(pd->propType())) { + const int typeId = pd->propType(); + if (isPrimitiveType(typeId)) { + return recordError( + binding->location, + tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".") + .arg(name) + .arg(QString::fromLatin1(QMetaType::typeName(typeId))) + ); + } + + if (!enginePrivate->propertyCacheForType(typeId)) { return recordError(binding->location, tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type") .arg(name) - .arg(QString::fromLatin1(QMetaType::typeName(pd->propType()))) + .arg(QString::fromLatin1(QMetaType::typeName(typeId))) ); } } @@ -679,15 +702,21 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope return noError; } - if (QQmlMetaType::isInterface(property->propType())) { + const int propType = property->propType(); + const auto rhsType = [&]() { + return stringAt(compilationUnit->objectAt(binding->value.objectIndex) + ->inheritedTypeNameIndex); + }; + + if (QQmlMetaType::isInterface(propType)) { // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. return noError; - } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) { + } else if (propType == QMetaType::QVariant || propType == qMetaTypeId<QJSValue>()) { // We can convert everything to QVariant :) return noError; } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType()); + const int listType = enginePrivate->listType(propType); if (!QQmlMetaType::isInterface(listType)) { QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { @@ -697,19 +726,23 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope return noError; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType())) { - auto typeName = QMetaType::typeName(property->propType()); + } else if (isPrimitiveType(propType)) { + auto typeName = QMetaType::typeName(propType); + return qQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting \"%3\"") + .arg(rhsType()) + .arg(propertyName) + .arg(typeName)); + } else if (QQmlValueTypeFactory::isValueType(propType)) { return qQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object") - .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>")) - .arg(propertyName)); - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { + .arg(rhsType()).arg(propertyName)); + } else if (propType == qMetaTypeId<QQmlScriptString>()) { return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { // We want to use the raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability // Using -1 for the minor version ensures that we get the raw metaObject. - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1); + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(propType, -1); if (propertyMetaObject) { // Will be true if the assigned type inherits propertyMetaObject @@ -723,11 +756,11 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope if (!isAssignable) { return qQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") - .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType())))); + .arg(rhsType()).arg(QLatin1String(QMetaType::typeName(propType)))); } } else { return qQmlCompileError(binding->valueLocation, tr("Cannot assign to property of unknown type \"%1\".") - .arg(QLatin1String(QMetaType::typeName(property->propType())))); + .arg(QLatin1String(QMetaType::typeName(propType)))); } } diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index 2a6831d898..3615749da1 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -154,27 +154,10 @@ bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, in return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; } -// returns the nearest _registered_ super class -QQmlType QQmlType::superType() const -{ - if (!d) - return QQmlType(); - if (!d->haveSuperType && d->baseMetaObject) { - const QMetaObject *mo = d->baseMetaObject->superClass(); - while (mo && !d->superType.isValid()) { - d->superType = QQmlMetaType::qmlType(mo, d->module, d->version_maj, d->version_min); - mo = mo->superClass(); - } - d->haveSuperType = true; - } - - return d->superType; -} - -QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const +QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) const { Q_ASSERT(isComposite()); - if (!engine || !d) + if (!engine) return QQmlType(); QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); if (td.isNull() || !td->isComplete()) @@ -184,7 +167,7 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const return QQmlMetaType::qmlType(mo); } -QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const +QQmlPropertyCache *QQmlTypePrivate::compositePropertyCache(QQmlEnginePrivate *engine) const { // similar logic to resolveCompositeBaseType Q_ASSERT(isComposite()); @@ -279,24 +262,30 @@ void QQmlTypePrivate::init() const lock.unlock(); } -void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const +void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const { - if ((isEnumFromBaseSetup || !baseMetaObject) - && (isEnumFromCacheSetup || !cache)) { + const QQmlPropertyCache *cache = (!isEnumFromCacheSetup && isComposite()) + ? compositePropertyCache(engine) + : nullptr; + + const QMetaObject *metaObject = !isEnumFromCacheSetup + ? baseMetaObject // beware: It could be a singleton type without metaobject + : nullptr; + + if (!cache && !metaObject) return; - } init(); QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); - if (!isEnumFromCacheSetup && cache) { + if (cache) { insertEnumsFromPropertyCache(cache); isEnumFromCacheSetup = true; } - if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject - insertEnums(baseMetaObject); + if (metaObject) { + insertEnums(metaObject); isEnumFromBaseSetup = true; } } @@ -565,7 +554,7 @@ bool QQmlType::isInterface() const bool QQmlType::isComposite() const { - return d && (d->regType == CompositeType || d->regType == CompositeSingletonType); + return d && d->isComposite(); } bool QQmlType::isCompositeSingleton() const @@ -634,7 +623,7 @@ QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivat QQmlType base; if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); + base = d->resolveCompositeBaseType(engine); return base.attachedPropertiesFunction(engine); } @@ -647,7 +636,7 @@ const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) c QQmlType base; if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); + base = d->resolveCompositeBaseType(engine); return base.attachedPropertiesType(engine); } @@ -666,7 +655,7 @@ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const QQmlType base; if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); + base = d->resolveCompositeBaseType(engine); return base.attachedPropertiesId(engine); } #endif @@ -706,24 +695,16 @@ int QQmlType::index() const QUrl QQmlType::sourceUrl() const { - if (d) { - if (d->regType == CompositeType) - return d->extraData.fd->url; - else if (d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->url; - } - return QUrl(); + return d ? d->sourceUrl() : QUrl(); } int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->enums.value(name); if (rv) @@ -738,11 +719,9 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->enums.value(name); if (rv) @@ -757,10 +736,9 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->enums.value(name); if (rv) @@ -775,10 +753,9 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(name); if (rv) @@ -793,10 +770,9 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bo { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(name); if (rv) @@ -845,10 +821,9 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scope { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); if (rv) { @@ -868,10 +843,9 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scope { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName)); if (rv) { diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h index 158fefad2c..ec27b38a73 100644 --- a/src/qml/qml/qqmltype_p.h +++ b/src/qml/qml/qqmltype_p.h @@ -182,12 +182,7 @@ public: }; private: - QQmlType superType() const; - QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; - int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; friend uint qHash(const QQmlType &t, uint seed); - QQmlRefPointer<const QQmlTypePrivate> d; }; diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h index d381e11df4..6a2d961de8 100644 --- a/src/qml/qml/qqmltype_p_p.h +++ b/src/qml/qml/qqmltype_p_p.h @@ -66,10 +66,30 @@ public: QQmlTypePrivate(QQmlType::RegistrationType type); void init() const; - void initEnums(const QQmlPropertyCache *cache = nullptr) const; + void initEnums(QQmlEnginePrivate *engine) const; void insertEnums(const QMetaObject *metaObject) const; void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; + QUrl sourceUrl() const + { + switch (regType) { + case QQmlType::CompositeType: + return extraData.fd->url; + case QQmlType::CompositeSingletonType: + return extraData.sd->singletonInstanceInfo->url; + default: + return QUrl(); + } + } + + bool isComposite() const + { + return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType; + } + + QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; + QQmlType::RegistrationType regType; struct QQmlCppTypeData diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index e7633a1bba..9a6bd73326 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -328,18 +328,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedType(binding->propertyNameIndex); QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (!type.isValid()) { - if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) { - if (type.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } + if (!type.isValid()) + imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr); const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); if (!attachedType) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 3a18bbf7c9..42e7d2c4b4 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -964,8 +964,10 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) { - if (path.isEmpty()) + const QChar nullChar(QChar::Null); + if (path.isEmpty() || path.contains(nullChar) || file.isEmpty() || file.contains(nullChar)) return false; + Q_ASSERT(path.endsWith(QLatin1Char('/'))); if (path.at(0) == QLatin1Char(':')) { // qrc resource diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 931f37b35a..ef4a628a04 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -476,6 +476,34 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, } // Fall through to base implementation } + + if (name->startsWithUpper()) { + bool ok = false; + int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + lookup->qmlEnumValueLookup.ic = This->internalClass(); + lookup->qmlEnumValueLookup.encodedEnumValue + = QV4::Value::fromInt32(value).asReturnedValue(); + lookup->getter = QQmlTypeWrapper::lookupEnumValue; + return lookup->getter(lookup, engine, *object); + } + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper( + scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + + lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); + lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper + = static_cast<Heap::Object*>(enumWrapper->heapObject()); + lookup->getter = QQmlTypeWrapper::lookupScopedEnum; + return enumWrapper.asReturnedValue(); + } + // Fall through to base implementation + } // Fall through to base implementation } return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); @@ -519,6 +547,34 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); } +ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlEnumValueLookup.ic) { + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return l->qmlEnumValueLookup.encodedEnumValue; +} + +ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + Scope scope(engine); + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); + + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) { + QQmlType::derefHandle(enumWrapper->d()->typePrivate); + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr; + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return enumWrapper.asReturnedValue(); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 6b51f421b3..7dc3f55310 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -115,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base); protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 2225191a9d..d83fc4bb48 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -65,11 +65,13 @@ struct QQmlValueTypeFactoryImpl QQmlValueType *valueTypes[QVariant::UserType]; QHash<int, QQmlValueType *> userTypes; QMutex mutex; + + QQmlValueType invalidValueType; }; QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { - std::fill_n(valueTypes, int(QVariant::UserType), nullptr); + std::fill_n(valueTypes, int(QVariant::UserType), &invalidValueType); #if QT_CONFIG(qml_itemmodel) // See types wrapped in qqmlmodelindexvaluetype_p.h @@ -79,20 +81,18 @@ QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl() { - qDeleteAll(valueTypes, valueTypes + QVariant::UserType); + for (QQmlValueType *type : valueTypes) { + if (type != &invalidValueType) + delete type; + } qDeleteAll(userTypes); } -bool QQmlValueTypeFactoryImpl::isValueType(int idx) +bool isInternalType(int idx) { - if (idx >= QMetaType::User) - return valueType(idx) != nullptr; - - if (idx < 0) - return false; - // Qt internal types switch (idx) { + case QMetaType::UnknownType: case QMetaType::QStringList: case QMetaType::QObjectStar: case QMetaType::VoidStar: @@ -101,12 +101,20 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx) case QMetaType::QLocale: case QMetaType::QImage: // scarce type, keep as QVariant case QMetaType::QPixmap: // scarce type, keep as QVariant - return false; - default: return true; + default: + return false; } } +bool QQmlValueTypeFactoryImpl::isValueType(int idx) +{ + if (idx < 0 || isInternalType(idx)) + return false; + + return valueType(idx) != nullptr; +} + const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) { switch (t) { @@ -168,15 +176,17 @@ QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx) } QQmlValueType *rv = valueTypes[idx]; - if (!rv) { + if (rv == &invalidValueType) { // No need for mutex protection - the most we can lose is a valueType instance // TODO: Investigate the performance/memory characteristics of // removing the preallocated array - if (const QMetaObject *mo = metaObjectForMetaType(idx)) { - rv = new QQmlValueType(idx, mo); - valueTypes[idx] = rv; - } + if (isInternalType(idx)) + rv = valueTypes[idx] = nullptr; + else if (const QMetaObject *mo = metaObjectForMetaType(idx)) + rv = valueTypes[idx] = new QQmlValueType(idx, mo); + else + rv = valueTypes[idx] = nullptr; } return rv; @@ -208,6 +218,13 @@ void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, #endif } +QQmlValueType::QQmlValueType() : + _metaObject(nullptr), + gadgetPtr(nullptr), + metaType(QMetaType::UnknownType) +{ +} + QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) : gadgetPtr(QMetaType::create(typeId)) , metaType(typeId) @@ -225,7 +242,7 @@ QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) QQmlValueType::~QQmlValueType() { QObjectPrivate *op = QObjectPrivate::get(this); - Q_ASSERT(op->metaObject == this); + Q_ASSERT(op->metaObject == nullptr || op->metaObject == this); op->metaObject = nullptr; ::free(const_cast<QMetaObject *>(_metaObject)); metaType.destroy(gadgetPtr); diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 75150b3f32..95ad81d045 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlValueType : public QObject, public QAbstractDynamicMetaObject { public: + QQmlValueType(); QQmlValueType(int userType, const QMetaObject *metaObject); ~QQmlValueType() override; void read(QObject *, int); @@ -92,7 +93,7 @@ public: class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory { public: - static bool isValueType(int); + static bool isValueType(int idx); static QQmlValueType *valueType(int idx); static const QMetaObject *metaObjectForMetaType(int type); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index cf6553d129..f23921497c 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,8 @@ 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; + r->d()->setValueType(QQmlValueTypeFactory::valueType(typeId)); + r->d()->setGadgetPtr(nullptr); return r->asReturnedValue(); } @@ -204,8 +204,8 @@ 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; + r->d()->setValueType(QQmlValueTypeFactory::valueType(typeId)); + r->d()->setGadgetPtr(nullptr); r->d()->setValue(value); return r->asReturnedValue(); } @@ -223,9 +223,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 +307,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 +315,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 +327,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 +354,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 +387,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 +412,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 @@ -604,7 +604,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v if (property.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double) v = v.toInt(); - void *gadget = r->d()->gadgetPtr; + void *gadget = r->d()->gadgetPtr(); property.writeOnGadget(gadget, v); @@ -620,7 +620,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/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index b9d8fed243..a67ac7384d 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -1180,6 +1180,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/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 2e213e7dc3..355150b786 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1941,30 +1941,32 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number"); QString context; - if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { - QString path = ctxt->urlString(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - int lastDot = path.lastIndexOf(QLatin1Char('.')); - int length = lastDot - (lastSlash + 1); - context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); - } else { - CppStackFrame *frame = scope.engine->currentStackFrame; - // The first non-empty source URL in the call stack determines the translation context. - while (frame && context.isEmpty()) { - if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) { - const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit); - QString fileName = unit->fileName(); - QUrl url(unit->fileName()); - if (url.isValid() && url.isRelative()) { - context = url.fileName(); - } else { - context = QQmlFile::urlToLocalFileOrQrc(fileName); - if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) - context = fileName; - } - context = QFileInfo(context).baseName(); + CppStackFrame *frame = scope.engine->currentStackFrame; + // The first non-empty source URL in the call stack determines the translation context. + while (frame && context.isEmpty()) { + if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) { + const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit); + QString fileName = unit->fileName(); + QUrl url(unit->fileName()); + if (url.isValid() && url.isRelative()) { + context = url.fileName(); + } else { + context = QQmlFile::urlToLocalFileOrQrc(fileName); + if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) + context = fileName; } - frame = frame->parent; + context = QFileInfo(context).baseName(); + } + frame = frame->parent; + } + + if (context.isEmpty()) { + if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { + QString path = ctxt->urlString(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + int lastDot = path.lastIndexOf(QLatin1Char('.')); + int length = lastDot - (lastSlash + 1); + context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); } } |