aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4engine.cpp14
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp86
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h60
-rw-r--r--src/qml/qml/ftw/qbipointer_p.h5
-rw-r--r--src/qml/qml/qqmldata_p.h4
-rw-r--r--src/qml/qml/qqmlengine.cpp2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/frozenQObject2.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp61
8 files changed, 179 insertions, 72 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 06ba98aad2..26677a37b9 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1855,16 +1855,10 @@ QV4::ReturnedValue ExecutionEngine::fromData(
a->setArrayLengthUnchecked(list.count());
return a.asReturnedValue();
} 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))
- return ret;
- QV4::ScopedValue v(scope, ret);
- if (auto obj = v->as<Object>()) {
- obj->setInternalClass(obj->internalClass()->cryopreserved());
- return obj->asReturnedValue();
- } else {
- return ret;
- }
+ if (flags.testFlag(QMetaType::IsConst))
+ return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr));
+ else
+ return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
}
bool succeeded = false;
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index ed51bdef88..b744e45f26 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -100,15 +100,10 @@ static ReturnedValue loadProperty(ExecutionEngine *v4, QObject *object,
if (property.isQObject()) {
QObject *rv = nullptr;
property.readProperty(object, &rv);
- ReturnedValue ret = QObjectWrapper::wrap(v4, rv);
- if (propMetaType.flags().testFlag(QMetaType::IsConst)) {
- ScopedValue v(scope, ret);
- if (auto obj = v->as<Object>()) {
- obj->setInternalClass(obj->internalClass()->cryopreserved());
- return obj->asReturnedValue();
- }
- }
- return ret;
+ if (propMetaType.flags().testFlag(QMetaType::IsConst))
+ return QObjectWrapper::wrapConst(v4, rv);
+ else
+ return QObjectWrapper::wrap(v4, rv);
}
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)
{
if (QQmlData::wasDeleted(object))
@@ -684,6 +702,8 @@ void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
ddata->jsWrapper.markOnce(markStack);
else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
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)
@@ -711,14 +731,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p
bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QObjectWrapper>());
- QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper *>(a);
- Object *o = b->as<Object>();
- if (o) {
- if (QQmlTypeWrapper *qmlTypeWrapper = o->as<QQmlTypeWrapper>())
- return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object();
- }
+ const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
+ if (const QQmlTypeWrapper *qmlTypeWrapper = b->as<QQmlTypeWrapper>())
+ return qmlTypeWrapper->object() == aobjectWrapper->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)
@@ -2463,39 +2482,20 @@ void QmlSignalHandler::initProto(ExecutionEngine *engine)
engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d();
}
-void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value)
-{
- QHash<QObject*, WeakValue>::operator[](key).set(value->internalClass->engine, value);
- connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
-}
-
-
-MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator 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);
+ const QObjectBiPointer key = it.key();
+ const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
+ disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
+ return QHash<QObjectBiPointer, WeakValue>::erase(it);
}
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
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 959ca21d46..ff569ce60d 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -140,6 +140,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
QObject *object, String *name, RevisionMode revisionMode, const Value &value);
static ReturnedValue wrap(ExecutionEngine *engine, QObject *object);
+ static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object);
static void markWrapper(QObject *object, MarkStack *markStack);
using Object::get;
@@ -181,6 +182,7 @@ protected:
private:
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)
@@ -197,6 +199,15 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje
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>
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);
};
+using QObjectBiPointer = QBiPointer<QObject, const QObject>;
+
class MultiplyWrappedQObjectMap : public QObject,
- private QHash<QObject*, QV4::WeakValue>
+ private QHash<QObjectBiPointer, QV4::WeakValue>
{
Q_OBJECT
public:
- typedef QHash<QObject*, QV4::WeakValue>::ConstIterator ConstIterator;
- typedef QHash<QObject*, QV4::WeakValue>::Iterator Iterator;
+ typedef QHash<QObjectBiPointer, QV4::WeakValue>::ConstIterator ConstIterator;
+ typedef QHash<QObjectBiPointer, QV4::WeakValue>::Iterator Iterator;
+
+ using value_type = QHash<QObjectBiPointer, QV4::WeakValue>::value_type;
- using value_type = QHash<QObject*, QV4::WeakValue>::value_type;
+ ConstIterator begin() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constBegin(); }
+ Iterator begin() { return QHash<QObjectBiPointer, QV4::WeakValue>::begin(); }
+ ConstIterator end() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constEnd(); }
+ Iterator end() { return QHash<QObjectBiPointer, QV4::WeakValue>::end(); }
- ConstIterator begin() const { return QHash<QObject*, QV4::WeakValue>::constBegin(); }
- Iterator begin() { return QHash<QObject*, QV4::WeakValue>::begin(); }
- ConstIterator end() const { return QHash<QObject*, QV4::WeakValue>::constEnd(); }
- Iterator end() { return QHash<QObject*, QV4::WeakValue>::end(); }
+ template<typename Pointer>
+ 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*)));
+ }
- void insert(QObject *key, Heap::Object *value);
- ReturnedValue value(QObject *key) const
+ template<typename Pointer>
+ ReturnedValue value(Pointer key) const
{
ConstIterator it = find(key);
return it == end()
@@ -317,8 +337,24 @@ public:
}
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:
void removeDestroyedObject(QObject*);
diff --git a/src/qml/qml/ftw/qbipointer_p.h b/src/qml/qml/ftw/qbipointer_p.h
index 79a6676ebd..4039d6b60d 100644
--- a/src/qml/qml/ftw/qbipointer_p.h
+++ b/src/qml/qml/ftw/qbipointer_p.h
@@ -87,6 +87,11 @@ public:
inline T *asT1() 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:
quintptr ptr_value = 0;
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index f72feb2ce6..5b7c05c61e 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -103,7 +103,9 @@ public:
// set when at least one of the object's properties is intercepted
quint32 hasInterceptorMetaObject: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
// bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 57df383257..4e12ab93d8 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -245,7 +245,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
QQmlData::QQmlData()
: ownMemory(true), indestructible(true), explicitIndestructibleSet(false),
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
- hasInterceptorMetaObject(false), hasVMEMetaObject(false),
+ hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false),
bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr),
bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr),
lineNumber(0), columnNumber(0), jsEngineId(0),
diff --git a/tests/auto/qml/qqmlecmascript/data/frozenQObject2.qml b/tests/auto/qml/qqmlecmascript/data/frozenQObject2.qml
new file mode 100644
index 0000000000..ebff0cde8b
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/frozenQObject2.qml
@@ -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";
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 4cc056a425..63cf12a5a2 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -9945,14 +9945,65 @@ void tst_qqmlecmascript::cmpInThrows()
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()
{
+ qmlRegisterType<FrozenObjects>("test", 1, 0, "FrozenObjects");
+
QQmlEngine engine;
- QQmlComponent component(&engine, testFileUrl("frozenQObject.qml"));
- QScopedPointer<QObject> root(component.create());
- QVERIFY2(root, qPrintable(component.errorString()));
- QVERIFY(root->property("caughtException").toBool());
- QVERIFY(root->property("nameCorrect").toBool());
+ QQmlComponent component1(&engine, testFileUrl("frozenQObject.qml"));
+ QScopedPointer<QObject> root1(component1.create());
+ QVERIFY2(root1, qPrintable(component1.errorString()));
+ QVERIFY(root1->property("caughtException").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