Qml: Allow const and non-const QObjectWrappers to coexist
We can access the same QObject in const and non-const contexts. Both should be possible. Store the const objectwrapper in m_multiplyWrappedObjects. That's somewhat slow, but const QObjects are rather rare. Pick-to: 6.4 Fixes: QTBUG-98479 Change-Id: I047afc121f5c29b955cd833e0a2c8299fc52b021 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
5cda89ce03
commit
d3b3fef5a8
|
@ -1855,16 +1855,10 @@ QV4::ReturnedValue ExecutionEngine::fromData(
|
||||||
a->setArrayLengthUnchecked(list.count());
|
a->setArrayLengthUnchecked(list.count());
|
||||||
return a.asReturnedValue();
|
return a.asReturnedValue();
|
||||||
} else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
|
} else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
|
||||||
QV4::ReturnedValue ret = QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
|
if (flags.testFlag(QMetaType::IsConst))
|
||||||
if (!flags.testFlag(QMetaType::IsConst))
|
return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr));
|
||||||
return ret;
|
else
|
||||||
QV4::ScopedValue v(scope, ret);
|
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
|
||||||
if (auto obj = v->as<Object>()) {
|
|
||||||
obj->setInternalClass(obj->internalClass()->cryopreserved());
|
|
||||||
return obj->asReturnedValue();
|
|
||||||
} else {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool succeeded = false;
|
bool succeeded = false;
|
||||||
|
|
|
@ -100,15 +100,10 @@ static ReturnedValue loadProperty(ExecutionEngine *v4, QObject *object,
|
||||||
if (property.isQObject()) {
|
if (property.isQObject()) {
|
||||||
QObject *rv = nullptr;
|
QObject *rv = nullptr;
|
||||||
property.readProperty(object, &rv);
|
property.readProperty(object, &rv);
|
||||||
ReturnedValue ret = QObjectWrapper::wrap(v4, rv);
|
if (propMetaType.flags().testFlag(QMetaType::IsConst))
|
||||||
if (propMetaType.flags().testFlag(QMetaType::IsConst)) {
|
return QObjectWrapper::wrapConst(v4, rv);
|
||||||
ScopedValue v(scope, ret);
|
else
|
||||||
if (auto obj = v->as<Object>()) {
|
return QObjectWrapper::wrap(v4, rv);
|
||||||
obj->setInternalClass(obj->internalClass()->cryopreserved());
|
|
||||||
return obj->asReturnedValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList))
|
if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList))
|
||||||
|
@ -670,6 +665,29 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObject *object)
|
||||||
|
{
|
||||||
|
const QObject *constObject = object;
|
||||||
|
|
||||||
|
QQmlData *ddata = QQmlData::get(object, true);
|
||||||
|
|
||||||
|
Scope scope(engine);
|
||||||
|
ScopedObject constWrapper(scope);
|
||||||
|
if (engine->m_multiplyWrappedQObjects && ddata->hasConstWrapper)
|
||||||
|
constWrapper = engine->m_multiplyWrappedQObjects->value(constObject);
|
||||||
|
|
||||||
|
if (!constWrapper) {
|
||||||
|
constWrapper = create(engine, object);
|
||||||
|
constWrapper->setInternalClass(constWrapper->internalClass()->cryopreserved());
|
||||||
|
if (!engine->m_multiplyWrappedQObjects)
|
||||||
|
engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
|
||||||
|
engine->m_multiplyWrappedQObjects->insert(constObject, constWrapper->d());
|
||||||
|
ddata->hasConstWrapper = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return constWrapper.asReturnedValue();
|
||||||
|
}
|
||||||
|
|
||||||
void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
|
void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
|
||||||
{
|
{
|
||||||
if (QQmlData::wasDeleted(object))
|
if (QQmlData::wasDeleted(object))
|
||||||
|
@ -684,6 +702,8 @@ void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
|
||||||
ddata->jsWrapper.markOnce(markStack);
|
ddata->jsWrapper.markOnce(markStack);
|
||||||
else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
|
else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
|
||||||
engine->m_multiplyWrappedQObjects->mark(object, markStack);
|
engine->m_multiplyWrappedQObjects->mark(object, markStack);
|
||||||
|
if (ddata->hasConstWrapper)
|
||||||
|
engine->m_multiplyWrappedQObjects->mark(static_cast<const QObject *>(object), markStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
|
void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
|
||||||
|
@ -711,14 +731,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p
|
||||||
bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
|
bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
|
||||||
{
|
{
|
||||||
Q_ASSERT(a->as<QObjectWrapper>());
|
Q_ASSERT(a->as<QObjectWrapper>());
|
||||||
QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper *>(a);
|
const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
|
||||||
Object *o = b->as<Object>();
|
if (const QQmlTypeWrapper *qmlTypeWrapper = b->as<QQmlTypeWrapper>())
|
||||||
if (o) {
|
return qmlTypeWrapper->object() == aobjectWrapper->object();
|
||||||
if (QQmlTypeWrapper *qmlTypeWrapper = o->as<QQmlTypeWrapper>())
|
|
||||||
return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
// We can have a const and a non-const wrapper for the same object.
|
||||||
|
const QObjectWrapper *bobjectWrapper = b->as<QObjectWrapper>();
|
||||||
|
return bobjectWrapper && aobjectWrapper->object() == bobjectWrapper->object();
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
|
ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
|
||||||
|
@ -2463,39 +2482,20 @@ void QmlSignalHandler::initProto(ExecutionEngine *engine)
|
||||||
engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d();
|
engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value)
|
|
||||||
|
MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(
|
||||||
|
MultiplyWrappedQObjectMap::Iterator it)
|
||||||
{
|
{
|
||||||
QHash<QObject*, WeakValue>::operator[](key).set(value->internalClass->engine, value);
|
const QObjectBiPointer key = it.key();
|
||||||
connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
|
const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
|
||||||
}
|
disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
|
||||||
|
return QHash<QObjectBiPointer, WeakValue>::erase(it);
|
||||||
|
|
||||||
|
|
||||||
MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it)
|
|
||||||
{
|
|
||||||
disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
|
|
||||||
return QHash<QObject*, WeakValue>::erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiplyWrappedQObjectMap::remove(QObject *key)
|
|
||||||
{
|
|
||||||
Iterator it = find(key);
|
|
||||||
if (it == end())
|
|
||||||
return;
|
|
||||||
erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack)
|
|
||||||
{
|
|
||||||
Iterator it = find(key);
|
|
||||||
if (it == end())
|
|
||||||
return;
|
|
||||||
it->markOnce(markStack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
|
void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
|
||||||
{
|
{
|
||||||
QHash<QObject*, WeakValue>::remove(object);
|
QHash<QObjectBiPointer, WeakValue>::remove(object);
|
||||||
|
QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QV4
|
} // namespace QV4
|
||||||
|
|
|
@ -140,6 +140,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
|
||||||
QObject *object, String *name, RevisionMode revisionMode, const Value &value);
|
QObject *object, String *name, RevisionMode revisionMode, const Value &value);
|
||||||
|
|
||||||
static ReturnedValue wrap(ExecutionEngine *engine, QObject *object);
|
static ReturnedValue wrap(ExecutionEngine *engine, QObject *object);
|
||||||
|
static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object);
|
||||||
static void markWrapper(QObject *object, MarkStack *markStack);
|
static void markWrapper(QObject *object, MarkStack *markStack);
|
||||||
|
|
||||||
using Object::get;
|
using Object::get;
|
||||||
|
@ -181,6 +182,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
|
Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
|
||||||
|
Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object)
|
inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object)
|
||||||
|
@ -197,6 +199,15 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje
|
||||||
return wrap_slowPath(engine, object);
|
return wrap_slowPath(engine, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unfortunately we still need a non-const QObject* here because QQmlData needs to register itself in QObjectPrivate.
|
||||||
|
inline ReturnedValue QObjectWrapper::wrapConst(ExecutionEngine *engine, QObject *object)
|
||||||
|
{
|
||||||
|
if (Q_UNLIKELY(QQmlData::wasDeleted(object)))
|
||||||
|
return QV4::Encode::null();
|
||||||
|
|
||||||
|
return wrapConst_slowPath(engine, object);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ReversalFunctor>
|
template <typename ReversalFunctor>
|
||||||
inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup)
|
inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup)
|
||||||
{
|
{
|
||||||
|
@ -292,23 +303,32 @@ struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
|
||||||
static void initProto(ExecutionEngine *v4);
|
static void initProto(ExecutionEngine *v4);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using QObjectBiPointer = QBiPointer<QObject, const QObject>;
|
||||||
|
|
||||||
class MultiplyWrappedQObjectMap : public QObject,
|
class MultiplyWrappedQObjectMap : public QObject,
|
||||||
private QHash<QObject*, QV4::WeakValue>
|
private QHash<QObjectBiPointer, QV4::WeakValue>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
typedef QHash<QObject*, QV4::WeakValue>::ConstIterator ConstIterator;
|
typedef QHash<QObjectBiPointer, QV4::WeakValue>::ConstIterator ConstIterator;
|
||||||
typedef QHash<QObject*, QV4::WeakValue>::Iterator Iterator;
|
typedef QHash<QObjectBiPointer, QV4::WeakValue>::Iterator Iterator;
|
||||||
|
|
||||||
using value_type = QHash<QObject*, QV4::WeakValue>::value_type;
|
using value_type = QHash<QObjectBiPointer, QV4::WeakValue>::value_type;
|
||||||
|
|
||||||
ConstIterator begin() const { return QHash<QObject*, QV4::WeakValue>::constBegin(); }
|
ConstIterator begin() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constBegin(); }
|
||||||
Iterator begin() { return QHash<QObject*, QV4::WeakValue>::begin(); }
|
Iterator begin() { return QHash<QObjectBiPointer, QV4::WeakValue>::begin(); }
|
||||||
ConstIterator end() const { return QHash<QObject*, QV4::WeakValue>::constEnd(); }
|
ConstIterator end() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constEnd(); }
|
||||||
Iterator end() { return QHash<QObject*, QV4::WeakValue>::end(); }
|
Iterator end() { return QHash<QObjectBiPointer, QV4::WeakValue>::end(); }
|
||||||
|
|
||||||
void insert(QObject *key, Heap::Object *value);
|
template<typename Pointer>
|
||||||
ReturnedValue value(QObject *key) const
|
void insert(Pointer key, Heap::Object *value)
|
||||||
|
{
|
||||||
|
QHash<QObjectBiPointer, WeakValue>::operator[](key).set(value->internalClass->engine, value);
|
||||||
|
connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Pointer>
|
||||||
|
ReturnedValue value(Pointer key) const
|
||||||
{
|
{
|
||||||
ConstIterator it = find(key);
|
ConstIterator it = find(key);
|
||||||
return it == end()
|
return it == end()
|
||||||
|
@ -317,8 +337,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator erase(Iterator it);
|
Iterator erase(Iterator it);
|
||||||
void remove(QObject *key);
|
|
||||||
void mark(QObject *key, MarkStack *markStack);
|
template<typename Pointer>
|
||||||
|
void remove(Pointer key)
|
||||||
|
{
|
||||||
|
Iterator it = find(key);
|
||||||
|
if (it == end())
|
||||||
|
return;
|
||||||
|
erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Pointer>
|
||||||
|
void mark(Pointer key, MarkStack *markStack)
|
||||||
|
{
|
||||||
|
Iterator it = find(key);
|
||||||
|
if (it == end())
|
||||||
|
return;
|
||||||
|
it->markOnce(markStack);
|
||||||
|
}
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void removeDestroyedObject(QObject*);
|
void removeDestroyedObject(QObject*);
|
||||||
|
|
|
@ -87,6 +87,11 @@ public:
|
||||||
inline T *asT1() const;
|
inline T *asT1() const;
|
||||||
inline T2 *asT2() const;
|
inline T2 *asT2() const;
|
||||||
|
|
||||||
|
friend size_t qHash(const QBiPointer<T, T2> &ptr, size_t seed = 0)
|
||||||
|
{
|
||||||
|
return qHash(ptr.isNull() ? quintptr(0) : ptr.ptr_value, seed);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
quintptr ptr_value = 0;
|
quintptr ptr_value = 0;
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,9 @@ public:
|
||||||
// set when at least one of the object's properties is intercepted
|
// set when at least one of the object's properties is intercepted
|
||||||
quint32 hasInterceptorMetaObject:1;
|
quint32 hasInterceptorMetaObject:1;
|
||||||
quint32 hasVMEMetaObject:1;
|
quint32 hasVMEMetaObject:1;
|
||||||
quint32 dummy:8;
|
// If we have another wrapper for a const QObject * in the multiply wrapped QObjects.
|
||||||
|
quint32 hasConstWrapper: 1;
|
||||||
|
quint32 dummy:7;
|
||||||
|
|
||||||
// When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
|
// When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
|
||||||
// bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
|
// bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
|
||||||
|
|
|
@ -245,7 +245,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
|
||||||
QQmlData::QQmlData()
|
QQmlData::QQmlData()
|
||||||
: ownMemory(true), indestructible(true), explicitIndestructibleSet(false),
|
: ownMemory(true), indestructible(true), explicitIndestructibleSet(false),
|
||||||
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
|
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
|
||||||
hasInterceptorMetaObject(false), hasVMEMetaObject(false),
|
hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false),
|
||||||
bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr),
|
bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr),
|
||||||
bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr),
|
bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr),
|
||||||
lineNumber(0), columnNumber(0), jsEngineId(0),
|
lineNumber(0), columnNumber(0), jsEngineId(0),
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import QtQml
|
||||||
|
import test
|
||||||
|
|
||||||
|
FrozenObjects {
|
||||||
|
id: app;
|
||||||
|
|
||||||
|
property bool caughtSignal: false
|
||||||
|
onFooMember2Emitted: caughtSignal = true
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
app.fooMember.name = "John";
|
||||||
|
app.fooMemberConst;
|
||||||
|
app.fooMember.name = "Jane";
|
||||||
|
|
||||||
|
app.fooMember2.name = "John";
|
||||||
|
app.triggerSignal();
|
||||||
|
app.fooMember2.name = "Jane";
|
||||||
|
}
|
||||||
|
}
|
|
@ -9945,14 +9945,65 @@ void tst_qqmlecmascript::cmpInThrows()
|
||||||
QCOMPARE(stacktrace.at(0), QStringLiteral("%entry:14:-1:file:foo.js"));
|
QCOMPARE(stacktrace.at(0), QStringLiteral("%entry:14:-1:file:foo.js"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FrozenFoo : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrozenFoo(QObject *parent = nullptr) : QObject(parent) {}
|
||||||
|
QString name() const { return m_name; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void nameChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_name{ "Foo" };
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrozenObjects : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(FrozenFoo *fooMember READ fooMember CONSTANT);
|
||||||
|
Q_PROPERTY(const FrozenFoo *fooMemberConst READ fooMemberConst CONSTANT);
|
||||||
|
Q_PROPERTY(FrozenFoo *fooMember2 READ fooMember2 CONSTANT);
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrozenObjects(QObject *parent = nullptr) : QObject(parent) {}
|
||||||
|
|
||||||
|
Q_INVOKABLE void triggerSignal() { emit fooMember2Emitted(&m_fooMember2); }
|
||||||
|
|
||||||
|
FrozenFoo *fooMember() { return &m_fooMember; }
|
||||||
|
FrozenFoo *fooMember2() { return &m_fooMember2; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fooMember2Emitted(const FrozenFoo *fooMember2);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const FrozenFoo *fooMemberConst() const { return &m_fooMember; }
|
||||||
|
|
||||||
|
FrozenFoo m_fooMember;
|
||||||
|
FrozenFoo m_fooMember2;
|
||||||
|
};
|
||||||
|
|
||||||
void tst_qqmlecmascript::frozenQObject()
|
void tst_qqmlecmascript::frozenQObject()
|
||||||
{
|
{
|
||||||
|
qmlRegisterType<FrozenObjects>("test", 1, 0, "FrozenObjects");
|
||||||
|
|
||||||
QQmlEngine engine;
|
QQmlEngine engine;
|
||||||
QQmlComponent component(&engine, testFileUrl("frozenQObject.qml"));
|
QQmlComponent component1(&engine, testFileUrl("frozenQObject.qml"));
|
||||||
QScopedPointer<QObject> root(component.create());
|
QScopedPointer<QObject> root1(component1.create());
|
||||||
QVERIFY2(root, qPrintable(component.errorString()));
|
QVERIFY2(root1, qPrintable(component1.errorString()));
|
||||||
QVERIFY(root->property("caughtException").toBool());
|
QVERIFY(root1->property("caughtException").toBool());
|
||||||
QVERIFY(root->property("nameCorrect").toBool());
|
QVERIFY(root1->property("nameCorrect").toBool());
|
||||||
|
|
||||||
|
QQmlComponent component2(&engine, testFileUrl("frozenQObject2.qml"));
|
||||||
|
QScopedPointer<QObject> root2(component2.create());
|
||||||
|
FrozenObjects *frozenObjects = qobject_cast<FrozenObjects *>(root2.data());
|
||||||
|
QVERIFY2(frozenObjects, qPrintable(component2.errorString()));
|
||||||
|
QVERIFY(frozenObjects->property("caughtSignal").toBool());
|
||||||
|
QCOMPARE(frozenObjects->fooMember()->name(), QStringLiteral("Jane"));
|
||||||
|
QCOMPARE(frozenObjects->fooMember2()->name(), QStringLiteral("Jane"));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConstPointer : QObject
|
struct ConstPointer : QObject
|
||||||
|
|
Loading…
Reference in New Issue