Fix crash with Component.onDestruction

A call to a handler of Component.onDestruction may end up causing WeakValues
such as QQmlData::jsWrapper to be set again, even though they've been set to
undefined in an earlier iteration of the loop that walks through the weak
references. That in turn may result in invalid object references to objects
that are scheduled for destruction by the collector.

So after calling all destroy handlers for QObjects, reset all of the weak
values again.

Task-number: QTBUG-54939
Change-Id: I00ebabb76274e296fb1bd90d8d3e21dbbb920b57
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Simon Hausmann 2016-07-25 16:07:16 +02:00
parent ff645deb25
commit 68bfc9332c
3 changed files with 117 additions and 1 deletions

View File

@ -428,7 +428,7 @@ void MemoryManager::sweep(bool lastSweep)
Managed *m = (*it).as<Managed>();
if (m->markBit())
continue;
// we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed
// we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed
// signal before we start sweeping the heap
if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>())
qobjectWrapper->destroyObject(lastSweep);
@ -436,6 +436,17 @@ void MemoryManager::sweep(bool lastSweep)
(*it) = Primitive::undefinedValue();
}
// onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure
// that they are all set to undefined.
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
if (!(*it).isManaged())
continue;
Managed *m = (*it).as<Managed>();
if (m->markBit())
continue;
(*it) = Primitive::undefinedValue();
}
// Now it is time to free QV4::QObjectWrapper Value, we must check the Value's tag to make sure its object has been destroyed
const int pendingCount = m_pendingFreedObjectWrapperValue.count();
if (pendingCount) {

View File

@ -0,0 +1,3 @@
import Test 1.0
WeakReferenceMutator {
}

View File

@ -49,6 +49,8 @@
#include <private/qv4scopedvalue_p.h>
#include <private/qv4alloca_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4object_p.h>
#include <private/qqmlcomponentattached_p.h>
#ifdef Q_CC_MSVC
#define NO_INLINE __declspec(noinline)
@ -287,6 +289,7 @@ private slots:
void replaceBinding();
void deleteRootObjectInCreation();
void onDestruction();
void onDestructionViaGC();
void bindingSuppression();
void signalEmitted();
void threadSignal();
@ -7157,6 +7160,105 @@ void tst_qqmlecmascript::onDestruction()
}
}
class WeakReferenceMutator : public QObject
{
Q_OBJECT
public:
WeakReferenceMutator()
: resultPtr(Q_NULLPTR)
, weakRef(Q_NULLPTR)
{}
void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr)
{
QV4::QObjectWrapper::wrap(v4, this);
QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
this->resultPtr = resultPtr;
this->weakRef = weakRef;
QObject::connect(QQmlComponent::qmlAttachedProperties(this), &QQmlComponentAttached::destruction, this, &WeakReferenceMutator::reviveFirstWeakReference);
}
private slots:
void reviveFirstWeakReference() {
*resultPtr = weakRef->valueRef() && weakRef->isNullOrUndefined();
if (!*resultPtr)
return;
QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(this));
weakRef->set(v4, v4->newObject());
*resultPtr = weakRef->valueRef() && !weakRef->isNullOrUndefined();
}
public:
bool *resultPtr;
QV4::WeakValue *weakRef;
};
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace Heap {
struct WeakReferenceSentinel : public Object {
WeakReferenceSentinel(WeakValue *weakRef, bool *resultPtr)
: weakRef(weakRef)
, resultPtr(resultPtr) {
}
~WeakReferenceSentinel() {
*resultPtr = weakRef->isNullOrUndefined();
}
WeakValue *weakRef;
bool *resultPtr;
};
} // namespace Heap
struct WeakReferenceSentinel : public Object {
V4_OBJECT2(WeakReferenceSentinel, Object)
V4_NEEDS_DESTROY
};
} // namespace QV4
QT_END_NAMESPACE
DEFINE_OBJECT_VTABLE(QV4::WeakReferenceSentinel);
void tst_qqmlecmascript::onDestructionViaGC()
{
qmlRegisterType<WeakReferenceMutator>("Test", 1, 0, "WeakReferenceMutator");
QQmlEngine engine;
QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine);
QQmlComponent component(&engine, testFileUrl("DestructionHelper.qml"));
QScopedPointer<QV4::WeakValue> weakRef;
bool mutatorResult = false;
bool sentinelResult = false;
{
weakRef.reset(new QV4::WeakValue);
weakRef->set(v4, v4->newObject());
QVERIFY(!weakRef->isNullOrUndefined());
QPointer<WeakReferenceMutator> weakReferenceMutator = qobject_cast<WeakReferenceMutator *>(component.create());
QVERIFY2(!weakReferenceMutator.isNull(), qPrintable(component.errorString()));
weakReferenceMutator->init(v4, weakRef.data(), &mutatorResult);
v4->memoryManager->allocObject<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult);
}
gc(engine);
QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC");
QVERIFY2(sentinelResult, "The weak reference was not cleared properly");
}
struct EventProcessor : public QObject
{
Q_OBJECT