diff options
-rw-r--r-- | src/qml/jsruntime/qv4lookup_p.h | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext.cpp | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqml.cpp | 341 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h | 78 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml | 34 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 34 |
8 files changed, 419 insertions, 82 deletions
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index ebce416172..d964fadd18 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -131,6 +131,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup { QQmlPropertyData *propertyData; } qobjectLookup; struct { + quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc + quintptr metaObject; // a (const QMetaObject* & 1) or nullptr + int coreIndex; + int notifyIndex; + } qobjectFallbackLookup; + struct { Heap::InternalClass *ic; quintptr metaObject; // a (const QMetaObject* & 1) or nullptr const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index c9a9ab138a..f7a5f897c8 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -640,6 +640,11 @@ ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, Executi return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); } +ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + return resolveQmlContextPropertyLookupGetter(l, engine, base); +} + ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base) { Q_UNUSED(base); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index cbbd04bff0..1da64b101f 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupIdObjectInParentContext(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 4efda0f5d5..ec7f50cf04 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -379,9 +379,9 @@ static QVector<QTypeRevision> availableRevisions(const QMetaObject *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); + for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount; + coreIndex < propertyEnd; ++coreIndex) { + const QMetaProperty property = metaObject->property(coreIndex); if (int revision = property.revision()) revisions.append(QTypeRevision::fromEncodedVersion(revision)); } @@ -761,6 +761,21 @@ void AOTCompiledContext::setReturnValueUndefined() const } } +static void captureFallbackProperty( + QObject *object, int coreIndex, int notifyIndex, bool isConstant, + QQmlContextData *qmlContext) +{ + if (!qmlContext || isConstant) + return; + + QQmlEngine *engine = qmlContext->engine(); + Q_ASSERT(engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + Q_ASSERT(ep); + if (QQmlPropertyCapture *capture = ep->propertyCapture) + capture->captureProperty(object, coreIndex, notifyIndex); +} + static void captureObjectProperty( QObject *object, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *property, QQmlContextData *qmlContext) @@ -787,8 +802,8 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach enum class ObjectPropertyResult { OK, NeedsInit, Deleted }; -static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object, void *target, - QQmlContextData *qmlContext) +static ObjectPropertyResult loadObjectProperty( + QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext) { QQmlData *qmlData = QQmlData::get(object); if (!qmlData) @@ -810,6 +825,33 @@ static ObjectPropertyResult loadObjectProperty(QV4::Lookup *l, QObject *object, return ObjectPropertyResult::OK; } +static ObjectPropertyResult loadFallbackProperty( + QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext) +{ + QQmlData *qmlData = QQmlData::get(object); + if (qmlData && qmlData->isQueuedForDeletion) + return ObjectPropertyResult::Deleted; + + Q_ASSERT(!QQmlData::wasDeleted(object)); + + const QMetaObject *metaObject + = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1); + if (!metaObject || metaObject != object->metaObject()) + return ObjectPropertyResult::NeedsInit; + + const int coreIndex = l->qobjectFallbackLookup.coreIndex; + if (qmlData && qmlData->hasPendingBindingBit(coreIndex)) + qmlData->flushPendingBinding(coreIndex); + + captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex, + l->qobjectFallbackLookup.isConstant, qmlContext); + + void *a[] = { target, nullptr }; + metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a); + + return ObjectPropertyResult::OK; +} + static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value) { const QQmlData *qmlData = QQmlData::get(object); @@ -826,7 +868,67 @@ static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, return ObjectPropertyResult::OK; } -static bool initObjectLookup( +static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value) +{ + const QQmlData *qmlData = QQmlData::get(object); + if (qmlData && qmlData->isQueuedForDeletion) + return ObjectPropertyResult::Deleted; + Q_ASSERT(!QQmlData::wasDeleted(object)); + + const QMetaObject *metaObject + = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1); + if (!metaObject || metaObject != object->metaObject()) + return ObjectPropertyResult::NeedsInit; + + const int coreIndex = l->qobjectFallbackLookup.coreIndex; + QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex)); + + void *args[] = { value, nullptr }; + metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args); + return ObjectPropertyResult::OK; +} + +static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType, + const AOTCompiledContext *aotContext) +{ + if (!lookupType.isValid()) { + // If type is invalid, then the calling code depends on the lookup + // to be set up in order to query the type, via lookupResultMetaType. + // We cannot verify the type in this case. + } else if ((lookupType.flags() & QMetaType::IsQmlList) + && (propertyType.flags() & QMetaType::IsQmlList)) { + // We want to check the value types here, but we cannot easily do it. + // Internally those are all QObject* lists, though. + } else if (lookupType.flags() & QMetaType::PointerToQObject) { + // We accept any base class as type, too + + const QMetaObject *typeMetaObject = lookupType.metaObject(); + const QMetaObject *foundMetaObject = propertyType.metaObject(); + if (!foundMetaObject) { + if (QQmlEngine *engine = aotContext->qmlEngine()) { + foundMetaObject = QQmlEnginePrivate::get(engine)->metaObjectForType( + propertyType).metaObject(); + } + } + + while (foundMetaObject && foundMetaObject != typeMetaObject) + foundMetaObject = foundMetaObject->superClass(); + + if (!foundMetaObject) + return false; + } else if (propertyType != lookupType) { + return false; + } + return true; +} + +enum class ObjectLookupResult { + Failure, + Object, + Fallback +}; + +static ObjectLookupResult initObjectLookup( const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type) { QV4::Scope scope(aotContext->engine->handle()); @@ -843,7 +945,7 @@ static bool initObjectLookup( QQmlData *ddata = QQmlData::get(object, true); Q_ASSERT(ddata); if (ddata->isQueuedForDeletion) - return false; + return ObjectLookupResult::Failure; QQmlPropertyData *property; if (!ddata->propertyCache) { @@ -854,45 +956,36 @@ static bool initObjectLookup( name.getPointer(), object, aotContext->qmlContext); } - if (!property) - return false; - - const QMetaType propType = property->propType(); - if (!type.isValid()) { - // If type is invalid, then the calling code depends on the lookup - // to be set up in order to query the type, via lookupResultMetaType. - // We cannot verify the type in this case. - } else if ((type.flags() & QMetaType::IsQmlList) && (propType.flags() & QMetaType::IsQmlList)) { - // We want to check the value types here, but we cannot easily do it. - // Internally those are all QObject* lists, though. - } else if (type.flags() & QMetaType::PointerToQObject) { - // We accept any base class as type, too - - const QMetaObject *typeMetaObject = type.metaObject(); - const QMetaObject *foundMetaObject = propType.metaObject(); - if (!foundMetaObject) { - if (QQmlEngine *engine = aotContext->qmlEngine()) { - foundMetaObject = QQmlEnginePrivate::get(engine)->metaObjectForType( - propType).metaObject(); - } - } - - while (foundMetaObject) { - if (foundMetaObject == typeMetaObject) - break; - foundMetaObject = foundMetaObject->superClass(); - } - - if (!foundMetaObject) - return false; - } else if (propType != type) { - return false; + if (!property) { + const QMetaObject *metaObject = object->metaObject(); + if (!metaObject) + return ObjectLookupResult::Failure; + + const int coreIndex = metaObject->indexOfProperty( + name->toQStringNoThrow().toUtf8().constData()); + if (coreIndex < 0) + return ObjectLookupResult::Failure; + + const QMetaProperty property = metaObject->property(coreIndex); + if (!isTypeCompatible(type, property.metaType(), aotContext)) + return ObjectLookupResult::Failure; + + l->releasePropertyCache(); + // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h + l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1; + l->qobjectFallbackLookup.coreIndex = coreIndex; + l->qobjectFallbackLookup.notifyIndex = property.notifySignalIndex(); + l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0; + return ObjectLookupResult::Fallback; } + if (!isTypeCompatible(type, property->propType(), aotContext)) + return ObjectLookupResult::Failure; + Q_ASSERT(ddata->propertyCache); QV4::setupQObjectLookup(l, ddata, property); - return true; + return ObjectLookupResult::Object; } static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit, @@ -928,29 +1021,47 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const return false; QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (l->getter != QV4::QQmlTypeWrapper::lookupSingletonProperty - && l->getter != QV4::Lookup::getterQObject) { - return false; + if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty + || l->getter == QV4::Lookup::getterQObject) { + const QQmlPropertyData *property = l->qobjectLookup.propertyData; + QQmlData::flushPendingBinding(object, property->coreIndex()); + captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext); + return true; } - const QQmlPropertyData *property = l->qobjectLookup.propertyData; - QQmlData::flushPendingBinding(object, property->coreIndex()); - captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext); + if (l->getter == QV4::Lookup::getterFallback) { + const int coreIndex = l->qobjectFallbackLookup.coreIndex; + QQmlData::flushPendingBinding(object, coreIndex); + captureFallbackProperty( + object, coreIndex, l->qobjectFallbackLookup.notifyIndex, + l->qobjectFallbackLookup.isConstant, qmlContext); + return true; + } + + return true; } bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const { QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty - && l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupContextObjectProperty) { - return false; + if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty + && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) { + const QQmlPropertyData *property = l->qobjectLookup.propertyData; + QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex()); + captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext); + return true; } - const QQmlPropertyData *property = l->qobjectLookup.propertyData; - QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex()); - captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, qmlContext); - return true; + if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) { + const int coreIndex = l->qobjectFallbackLookup.coreIndex; + QQmlData::flushPendingBinding(qmlScopeObject, coreIndex); + captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex, + l->qobjectFallbackLookup.isConstant, qmlContext); + return true; + } + + return false; } QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const @@ -971,6 +1082,14 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton || l->getter == QV4::QObjectWrapper::lookupAttached) { return QMetaType::fromType<QObject *>(); + } else if (l->getter == QV4::Lookup::getterFallback + || l->setter == QV4::Lookup::setterFallback + || l->qmlContextPropertyGetter + == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) { + const QMetaObject *metaObject + = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1); + const int coreIndex = l->qobjectFallbackLookup.coreIndex; + return metaObject->property(coreIndex).metaType(); } return QMetaType(); } @@ -984,9 +1103,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType QV4::Lookup l; l.clear(); l.nameIndex = nameIndex; - if (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) { - - ObjectPropertyResult storeResult; + ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit; + switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) { + case ObjectLookupResult::Object: { const QMetaType propType = l.qobjectLookup.propertyData->propType(); if (type == propType) { storeResult = storeObjectProperty(&l, qmlScopeObject, value); @@ -996,20 +1115,38 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType storeResult = storeObjectProperty(&l, qmlScopeObject, var.data()); } - switch (storeResult) { - case ObjectPropertyResult::NeedsInit: - engine->handle()->throwTypeError(); - break; - case ObjectPropertyResult::Deleted: - engine->handle()->throwTypeError( - QStringLiteral("Value is null and could not be converted to an object")); - break; - case ObjectPropertyResult::OK: - break; - } l.qobjectLookup.propertyCache->release(); - } else { + break; + } + case ObjectLookupResult::Fallback: { + const QMetaObject *metaObject + = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1); + const QMetaType propType + = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType(); + if (type == propType) { + storeResult = storeFallbackProperty(&l, qmlScopeObject, value); + } else { + QVariant var(propType); + propType.convert(type, value, propType, var.data()); + storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data()); + } + break; + } + case ObjectLookupResult::Failure: engine->handle()->throwTypeError(); + return; + } + + switch (storeResult) { + case ObjectPropertyResult::NeedsInit: + engine->handle()->throwTypeError(); + break; + case ObjectPropertyResult::Deleted: + engine->handle()->throwTypeError( + QStringLiteral("Value is null and could not be converted to an object")); + break; + case ObjectPropertyResult::OK: + break; } } @@ -1174,10 +1311,16 @@ void AOTCompiledContext::initLoadGlobalLookup(uint index) const bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const { QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty) + + ObjectPropertyResult result = ObjectPropertyResult::NeedsInit; + if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty) + result = loadObjectProperty(l, qmlScopeObject, target, qmlContext); + else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) + result = loadFallbackProperty(l, qmlScopeObject, target, qmlContext); + else return false; - switch (loadObjectProperty(l, qmlScopeObject, target, qmlContext)) { + switch (result) { case ObjectPropertyResult::NeedsInit: return false; case ObjectPropertyResult::Deleted: @@ -1198,12 +1341,22 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType QV4::ExecutionEngine *v4 = engine->handle(); QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (v4->hasException) + if (v4->hasException) { amendException(v4); - else if (initObjectLookup(this, l, qmlScopeObject, type)) + return; + } + + switch (initObjectLookup(this, l, qmlScopeObject, type)) { + case ObjectLookupResult::Object: l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty; - else + break; + case ObjectLookupResult::Fallback: + l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty; + break; + case ObjectLookupResult::Failure: v4->throwTypeError(); + break; + } } bool AOTCompiledContext::loadSingletonLookup(uint index, void *target) const @@ -1343,10 +1496,15 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ if (!object) return doThrow(); - if (l->getter != QV4::Lookup::getterQObject) + ObjectPropertyResult result = ObjectPropertyResult::NeedsInit; + if (l->getter == QV4::Lookup::getterQObject) + result = loadObjectProperty(l, object, target, qmlContext); + else if (l->getter == QV4::Lookup::getterFallback) + result = loadFallbackProperty(l, object, target, qmlContext); + else return false; - switch (loadObjectProperty(l, object, target, qmlContext)) { + switch (result) { case ObjectPropertyResult::Deleted: return doThrow(); case ObjectPropertyResult::NeedsInit: @@ -1366,10 +1524,17 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT amendException(v4); } else { QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (initObjectLookup(this, l, object, type)) + switch (initObjectLookup(this, l, object, type)) { + case ObjectLookupResult::Object: l->getter = QV4::Lookup::getterQObject; - else + break; + case ObjectLookupResult::Fallback: + l->getter = QV4::Lookup::getterFallback; + break; + case ObjectLookupResult::Failure: engine->handle()->throwTypeError(); + break; + } } } @@ -1437,10 +1602,15 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu return doThrow(); QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (l->setter != QV4::Lookup::setterQObject) + ObjectPropertyResult result = ObjectPropertyResult::NeedsInit; + if (l->setter == QV4::Lookup::setterQObject) + result = storeObjectProperty(l, object, value); + else if (l->setter == QV4::Lookup::setterFallback) + result = storeFallbackProperty(l, object, value); + else return false; - switch (storeObjectProperty(l, object, value)) { + switch (result) { case ObjectPropertyResult::Deleted: return doThrow(); case ObjectPropertyResult::NeedsInit: @@ -1460,10 +1630,17 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT amendException(v4); } else { QV4::Lookup *l = compilationUnit->runtimeLookups + index; - if (initObjectLookup(this, l, object, type)) + switch (initObjectLookup(this, l, object, type)) { + case ObjectLookupResult::Object: l->setter = QV4::Lookup::setterQObject; - else + break; + case ObjectLookupResult::Fallback: + l->setter = QV4::Lookup::setterFallback; + break; + case ObjectLookupResult::Failure: engine->handle()->throwTypeError(); + break; + } } } diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 3d05ffcf1d..4d585b62f0 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -1,6 +1,7 @@ set(cpp_sources birthdayparty.cpp birthdayparty.h cppbaseclass.h + dynamicmeta.h objectwithmethod.h person.cpp person.h theme.cpp theme.h @@ -61,6 +62,7 @@ set(qml_files excessiveParameters.qml extendedTypes.qml failures.qml + fallbacklookups.qml fileDialog.qml functionLookup.qml funcWithParams.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h new file mode 100644 index 0000000000..3f02e460e7 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h @@ -0,0 +1,78 @@ +#ifndef DYNAMICMETA_H +#define DYNAMICMETA_H + +#include <private/qobject_p.h> +#include <QtQmlIntegration/qqmlintegration.h> + +struct FreeDeleter { + void operator()(QMetaObject *meta) { free(meta); } +}; + +template<typename T> +class MetaObjectData : public QDynamicMetaObjectData +{ + Q_DISABLE_COPY_MOVE(MetaObjectData) +public: + MetaObjectData() = default; + ~MetaObjectData() = default; + + QMetaObject *toDynamicMetaObject(QObject *) override + { + return const_cast<QMetaObject *>(&T::staticMetaObject); + } + int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override + { + return o->qt_metacall(call, idx, argv); + } +}; + +class DynamicMeta : public QObject +{ + Q_OBJECT + Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL) + QML_ELEMENT +public: + + DynamicMeta(QObject *parent = nullptr) + : QObject(parent) + { + // deletes itself + QObjectPrivate::get(this)->metaObject = new MetaObjectData<DynamicMeta>; + } + + int foo() const { return m_foo; } + void setFoo(int newFoo) + { + if (m_foo != newFoo) { + m_foo = newFoo; + emit fooChanged(); + } + } + + Q_INVOKABLE int bar(int baz) { return baz + 12; } + +Q_SIGNALS: + void fooChanged(); + +private: + int m_foo = 0; +}; + +class DynamicMetaSingleton : public DynamicMeta +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + Q_PROPERTY(DynamicMetaSingleton *itself READ itself CONSTANT FINAL) +public: + DynamicMetaSingleton(QObject *parent = nullptr) : DynamicMeta(parent) + { + QObjectPrivate *d = QObjectPrivate::get(this); + delete d->metaObject; + d->metaObject = new MetaObjectData<DynamicMetaSingleton>; + } + + DynamicMetaSingleton *itself() { return this; } +}; + +#endif // DYNAMICMETA_H diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml b/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml new file mode 100644 index 0000000000..4b58cd344d --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml @@ -0,0 +1,34 @@ +import TestTypes +import QtQml + +DynamicMeta { + id: self + + function getSingleton(): QtObject { + return DynamicMetaSingleton.itself + } + + function withContext(): int { + foo = 93; + objectName = "aa" + foo; + return bar(4); + } + + function withId(): int { + self.foo = 94; + self.objectName = "bb" + foo; + return self.bar(5); + } + + function withSingleton(): int { + DynamicMetaSingleton.foo = 95; + DynamicMetaSingleton.objectName = "cc" + DynamicMetaSingleton.foo; + return DynamicMetaSingleton.bar(6); + } + + function withProperty(): int { + DynamicMetaSingleton.itself.foo = 96; + DynamicMetaSingleton.itself.objectName = "dd" + DynamicMetaSingleton.itself.foo; + return DynamicMetaSingleton.itself.bar(7); + } +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index d1b8565afc..acc117d967 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -124,6 +124,7 @@ private slots: void functionLookup(); void objectInVar(); void testIsnan(); + void fallbackLookups(); }; void tst_QmlCppCodegen::simpleBinding() @@ -1846,6 +1847,39 @@ void tst_QmlCppCodegen::testIsnan() QVERIFY(b.toBool()); } +void tst_QmlCppCodegen::fallbackLookups() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/TestTypes/fallbacklookups.qml"_qs); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->objectName(), QString()); + int result = 0; + + QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 16); + QCOMPARE(o->objectName(), QStringLiteral("aa93")); + + QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 17); + QCOMPARE(o->objectName(), QStringLiteral("bb94")); + + QObject *singleton = nullptr; + QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton)); + QVERIFY(singleton); + + QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 18); + QCOMPARE(singleton->objectName(), QStringLiteral("cc95")); + + QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 19); + QCOMPARE(singleton->objectName(), QStringLiteral("dd96")); +} + void tst_QmlCppCodegen::runInterpreted() { if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER")) |