diff options
author | Kent Hansen <kent.hansen@nokia.com> | 2012-06-28 15:29:22 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-07-03 19:30:46 +0200 |
commit | 378416af7542e34196908896c9a69573b995adb0 (patch) | |
tree | 750186a60e69cf0e9335b23ce3e1ca245f176de2 /src/script/bridge | |
parent | b9a5e257882c6418e28a82b9af1e9a6ac3044aea (diff) | |
download | qtscript-378416af7542e34196908896c9a69573b995adb0.tar.gz |
Ensure QObject wrappers are garbage-collected if appropriate
Address the "###" comment.
When QScriptEngine::newQObject() is called with the option
PreferExistingWrapperObject, the resulting wrapper object is cached
by the engine, in case it will be needed later (e.g., by a subsequent
newQObject() call for the same QObject, with the same wrap options).
But if a QObject wrapper object is only referenced by the QtScript
internals (i.e., not reachable from the JSC stack/heap, or kept in a
QScriptValue), the wrapper should not be kept alive if the ownership
is ScriptOwnership, or if the ownership is AutoOwnership and the C++
object has no parent.
If the wrapper is marked in that case, it won't get collected, and
hence the C++ object will be kept alive, too. In practice, QtScript
appears to leak memory (the objects will only be destroyed when the
engine is destroyed).
Our copy of JSC doesn't have a concept of weak references; the
ClientData callback in the JSC markRoots() function (which causes
QScriptEnginePrivate::mark() to be called) was moved to the end. This
enables the wrapper and connection marking logic to determine whether
a wrapper can be safely discarded (if it hasn't been marked by JSC by
this point, it must be a weak reference).
Task-number: QTBUG-21993
Change-Id: I61f6aafc91f080b80d3f5859148e645b80d9b653
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Diffstat (limited to 'src/script/bridge')
-rw-r--r-- | src/script/bridge/qscriptqobject.cpp | 18 | ||||
-rw-r--r-- | src/script/bridge/qscriptqobject_p.h | 20 |
2 files changed, 33 insertions, 5 deletions
diff --git a/src/script/bridge/qscriptqobject.cpp b/src/script/bridge/qscriptqobject.cpp index 2bc2547..15288a7 100644 --- a/src/script/bridge/qscriptqobject.cpp +++ b/src/script/bridge/qscriptqobject.cpp @@ -97,7 +97,7 @@ struct QObjectConnection QObjectDelegate *inst = static_cast<QObjectDelegate*>(delegate); if ((inst->ownership() == QScriptEngine::ScriptOwnership) || ((inst->ownership() == QScriptEngine::AutoOwnership) - && inst->value() && !inst->value()->parent())) { + && !inst->hasParent())) { senderWrapper = JSC::JSValue(); } else { markStack.append(senderWrapper); @@ -2199,6 +2199,10 @@ QObjectData::~QObjectData() } } +// This function assumes all objects reachable elsewhere in the JS environment +// (stack, heap) have been marked already (see QScriptEnginePrivate::mark()). +// This determines whether any of QtScript's internal QObject wrappers are only +// weakly referenced and can be discarded. void QObjectData::mark(JSC::MarkStack& markStack) { if (connectionManager) @@ -2207,10 +2211,14 @@ void QObjectData::mark(JSC::MarkStack& markStack) QList<QScript::QObjectWrapperInfo>::iterator it; for (it = wrappers.begin(); it != wrappers.end(); ) { const QScript::QObjectWrapperInfo &info = *it; - // ### don't mark if there are no other references. - // we need something like isMarked() - markStack.append(info.object); - ++it; + if (JSC::Heap::isCellMarked(info.object)) { + ++it; + } else if (info.isCollectableWhenWeaklyReferenced()) { + it = wrappers.erase(it); + } else { + markStack.append(info.object); + ++it; + } } } } diff --git a/src/script/bridge/qscriptqobject_p.h b/src/script/bridge/qscriptqobject_p.h index 837368e..c6ebc8a 100644 --- a/src/script/bridge/qscriptqobject_p.h +++ b/src/script/bridge/qscriptqobject_p.h @@ -95,6 +95,7 @@ public: inline QObject *value() const { return data->value; } inline void setValue(QObject* value) { data->value = value; } + inline bool hasParent() const { return data->value && data->value->parent(); } inline QScriptEngine::ValueOwnership ownership() const { return data->ownership; } @@ -138,6 +139,25 @@ struct QObjectWrapperInfo QScriptObject *object; QScriptEngine::ValueOwnership ownership; QScriptEngine::QObjectWrapOptions options; + + // Returns true if this wrapper can be garbage-collected when there are no + // other references to it in the JS environment (weak reference), otherwise + // returns false (should not be collected). + bool isCollectableWhenWeaklyReferenced() const + { + switch (ownership) { + case QScriptEngine::ScriptOwnership: + return true; + case QScriptEngine::AutoOwnership: { + QScriptObjectDelegate *delegate = object->delegate(); + Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject)); + return !static_cast<QObjectDelegate *>(delegate)->hasParent(); + } + default: + break; + } + return false; + } }; class QObjectData // : public QObjectUserData |