diff options
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 27 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 1 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/testtypes.cpp | 60 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/testtypes.h | 45 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 28 |
6 files changed, 157 insertions, 8 deletions
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 60a0829b9b..dacaa14a3d 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -277,12 +277,12 @@ bool QQmlTypeCompiler::compile() if (qmlType->parserStatusCast() != -1) ++parserStatusCount; } + ++objectCount; if (typeRef->component) { bindingCount += typeRef->component->totalBindingsCount; parserStatusCount += typeRef->component->totalParserStatusCount; objectCount += typeRef->component->totalObjectCount; - } else - ++objectCount; + } } } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 36c7dfb0e9..1de467b0fa 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -96,6 +96,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile sharedState->allCreatedBindings.allocate(compiledData->totalBindingsCount); sharedState->allParserStatusCallbacks.allocate(compiledData->totalParserStatusCount); sharedState->allCreatedObjects.allocate(compiledData->totalObjectCount); + sharedState->allJavaScriptObjects = 0; sharedState->creationContext = creationContext; sharedState->rootContext = 0; @@ -195,6 +196,13 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI sharedState->rootContext->isRootObjectInCreation = true; } + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::Scope scope(v4); + + Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator); + if (topLevelCreator) + sharedState->allJavaScriptObjects = scope.alloc(compiledData->totalObjectCount); + QVector<QQmlContextData::ObjectIdMapping> mapping(objectIndexToId.count()); for (QHash<int, int>::ConstIterator it = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); it != end; ++it) { @@ -208,8 +216,6 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context->setIdPropertyData(mapping); if (subComponentIndex == -1) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - QV4::Scope scope(v4); QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->scripts.count())); context->importedScripts = scripts; for (int i = 0; i < compiledData->scripts.count(); ++i) { @@ -230,6 +236,9 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI ddata->compiledData->addref(); } + if (topLevelCreator) + sharedState->allJavaScriptObjects = 0; + phase = CreatingObjectsPhase2; if (interrupt && interrupt->shouldInterrupt()) @@ -258,8 +267,11 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope valueScope(v4); - QV4::ScopedValue scopeObjectProtector(valueScope, declarativeData->jsWrapper.value()); - Q_UNUSED(scopeObjectProtector); + + Q_ASSERT(topLevelCreator); + Q_ASSERT(!sharedState->allJavaScriptObjects); + sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->totalObjectCount); + QV4::ScopedObject qmlScope(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _scopeObject)); QV4::Scoped<QV4::QmlBindingWrapper> qmlBindingWrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, qmlScope)); QV4::ExecutionContext *qmlContext = qmlBindingWrapper->context(); @@ -1150,9 +1162,12 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_scopeObject, scopeObject); QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + Q_ASSERT(sharedState->allJavaScriptObjects); + QV4::ValueRef ref = QV4::ValueRef::fromRawValue(sharedState->allJavaScriptObjects++); + ref = QV4::QObjectWrapper::wrap(v4, instance); + QV4::Scope valueScope(v4); - QV4::ScopedValue scopeObjectProtector(valueScope, ddata ? ddata->jsWrapper.value() : 0); - Q_UNUSED(scopeObjectProtector); QV4::ScopedObject qmlScope(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _scopeObject)); QV4::Scoped<QV4::QmlBindingWrapper> qmlBindingWrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, qmlScope)); QV4::ExecutionContext *qmlContext = qmlBindingWrapper->context(); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index ad2d67624f..fb4d71d054 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -64,6 +64,7 @@ struct QQmlObjectCreatorSharedState : public QSharedData QFiniteStack<QQmlAbstractBinding*> allCreatedBindings; QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks; QFiniteStack<QObject*> allCreatedObjects; + QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase. QQmlComponentAttached *componentAttached; QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; QQmlVmeProfiler profiler; diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index eb06b9e57d..560d86005c 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -300,6 +300,62 @@ static QObject *create_singletonWithEnum(QQmlEngine *, QJSEngine *) return new SingletonWithEnum; } +QObjectContainer::QObjectContainer() + : widgetParent(0) + , gcOnAppend(false) +{} + +QQmlListProperty<QObject> QObjectContainer::data() +{ + return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear); +} + +void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject *o) +{ + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + that->dataChildren.append(o); + QObject::connect(o, SIGNAL(destroyed(QObject*)), prop->object, SLOT(childDestroyed(QObject*))); + + if (that->gcOnAppend) { + QQmlEngine *engine = qmlEngine(that); + engine->collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } +} + +int QObjectContainer::children_count(QQmlListProperty<QObject> *prop) +{ + return static_cast<QObjectContainer*>(prop->object)->dataChildren.count(); +} + +QObject *QObjectContainer::children_at(QQmlListProperty<QObject> *prop, int index) +{ + return static_cast<QObjectContainer*>(prop->object)->dataChildren.at(index); +} + +void QObjectContainer::children_clear(QQmlListProperty<QObject> *prop) +{ + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + foreach (QObject *c, that->dataChildren) + QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); + that->dataChildren.clear(); +} + +void QObjectContainer::childDestroyed(QObject *child) { + dataChildren.removeAll(child); +} + +void FloatingQObject::classBegin() +{ + setParent(0); +} + +void FloatingQObject::componentComplete() +{ + Q_ASSERT(!parent()); +} + void registerTypes() { qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObjectAlias"); @@ -381,6 +437,10 @@ void registerTypes() qmlRegisterSingletonType<testImportOrderApi>("Qt.test.importOrderApi2",1,0,"Data",testImportOrder_api2); qmlRegisterSingletonType<SingletonWithEnum>("Qt.test.singletonWithEnum", 1, 0, "SingletonWithEnum", create_singletonWithEnum); + + qmlRegisterType<QObjectContainer>("Qt.test", 1, 0, "QObjectContainer"); + qmlRegisterType<QObjectContainerWithGCOnAppend>("Qt.test", 1, 0, "QObjectContainerWithGCOnAppend"); + qmlRegisterType<FloatingQObject>("Qt.test", 1, 0, "FloatingQObject"); } #include "testtypes.moc" diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index 928d594f62..d5a1220f23 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1661,6 +1661,51 @@ public: }; }; +// Like QtObject, but with default property +class QObjectContainer : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false) +public: + QObjectContainer(); + + QQmlListProperty<QObject> data(); + + static void children_append(QQmlListProperty<QObject> *prop, QObject *o); + static int children_count(QQmlListProperty<QObject> *prop); + static QObject *children_at(QQmlListProperty<QObject> *prop, int index); + static void children_clear(QQmlListProperty<QObject> *prop); + + QList<QObject*> dataChildren; + QWidget *widgetParent; + bool gcOnAppend; + +protected slots: + void childDestroyed(QObject *child); +}; + +class QObjectContainerWithGCOnAppend : public QObjectContainer +{ + Q_OBJECT +public: + QObjectContainerWithGCOnAppend() + { + gcOnAppend = true; + } +}; + +class FloatingQObject : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) +public: + FloatingQObject() {} + + virtual void classBegin(); + virtual void componentComplete(); +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a1e36b42e6..a9486a8e63 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -322,6 +322,7 @@ private slots: void varPropertyAccessOnObjectWithInvalidContext(); void importedScriptsAccessOnObjectWithInvalidContext(); void contextObjectOnLazyBindings(); + void garbageCollectionDuringCreation(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -7603,6 +7604,33 @@ void tst_qqmlecmascript::contextObjectOnLazyBindings() QCOMPARE(subObject->property("testValue").toInt(), int(42)); } +void tst_qqmlecmascript::garbageCollectionDuringCreation() +{ + QQmlComponent component(&engine); + component.setData("import Qt.test 1.0\n" + "QObjectContainerWithGCOnAppend {\n" + " objectName: \"root\"\n" + " FloatingQObject {\n" + " objectName: \"parentLessChild\"\n" + " property var blah;\n" // Ensure we have JS wrapper + " }\n" + "}\n", + QUrl()); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast<QObjectContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QObject *child = container->dataChildren.first(); + QQmlData *ddata = QQmlData::get(child); + QVERIFY(!ddata->jsWrapper.isNullOrUndefined()); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 0); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" |