aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@theqtcompany.com>2016-05-11 15:22:51 +0200
committerSimon Hausmann <simon.hausmann@theqtcompany.com>2016-05-18 10:08:30 +0000
commit72515ebe5a63c201fde09471bc646dbe15110a6b (patch)
tree22588014a613d0c2d72b909e28b1667a1c5dc9ec
parent74628af12aa8f72a56635bf27e7361861c50f796 (diff)
Fix crashes when incubating objects asynchronously with initial properties
This is a regression from commit 94e337fa95425d259e81b4d21f4d0853108553bd where we accidentally ended up not having a calling QML context set anymore when initializing the properties on newly incubated objects as provided by the caller. The QML context is necessary as for example when we set a URL property, the URL can be relative and it will be resolved to the base url of the context when written, such as in in QQmlPropertyPrivate::write. Change-Id: I1d896381fc92f653a7d76f4d82174bca48828f5e Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
-rw-r--r--src/qml/jsruntime/qv4context_p.h2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2
-rw-r--r--src/qml/qml/qqmlcomponent.cpp23
-rw-r--r--src/qml/qml/qqmlcomponent_p.h2
-rw-r--r--src/quick/items/qquickloader.cpp4
-rw-r--r--src/quick/items/qquickloader_p_p.h1
-rw-r--r--tests/auto/qml/qqmlcomponent/data/callingQmlContext.qml13
-rw-r--r--tests/auto/qml/qqmlcomponent/data/callingQmlContextComponent.qml3
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp59
9 files changed, 99 insertions, 10 deletions
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 6c360e7dda..c0c89a5c3d 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -241,7 +241,7 @@ struct WithContext : public ExecutionContext
V4_MANAGED(WithContext, ExecutionContext)
};
-struct QmlContext : public ExecutionContext
+struct Q_QML_EXPORT QmlContext : public ExecutionContext
{
V4_MANAGED(QmlContext, ExecutionContext)
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 49b6dce697..fefc5b6308 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -693,7 +693,7 @@ Heap::QmlContext *ExecutionEngine::qmlContext() const
if (ctx->type == Heap::ExecutionContext::Type_SimpleCallContext && !ctx->outer)
ctx = parentContext(currentContext)->d();
- if (!ctx->outer)
+ if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !ctx->outer)
return 0;
while (ctx->outer && ctx->outer->type != Heap::ExecutionContext::Type_GlobalContext)
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 9b52a951af..24abf52e38 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -1071,6 +1071,7 @@ struct QmlIncubatorObject : Object {
QPointer<QObject> parent;
QV4::Value valuemap;
QV4::Value statusChanged;
+ Pointer<Heap::QmlContext> qmlContext;
};
}
@@ -1185,7 +1186,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
*/
-static void setInitialProperties(QV4::ExecutionEngine *engine, const QV4::Value &o, const QV4::Value &v)
+static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v)
{
QV4::Scope scope(engine);
QV4::ScopedObject object(scope);
@@ -1196,6 +1197,9 @@ static void setInitialProperties(QV4::ExecutionEngine *engine, const QV4::Value
if (engine->hasException)
return;
+ QV4::ExecutionContextSaver saver(scope);
+ engine->pushContext(qmlContext);
+
while (1) {
name = it.nextPropertyNameAsString(val);
if (!name)
@@ -1269,8 +1273,10 @@ void QQmlComponent::createObject(QQmlV4Function *args)
QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4, rv));
Q_ASSERT(object->isObject());
- if (!valuemap->isUndefined())
- setInitialProperties(v4, object, valuemap);
+ if (!valuemap->isUndefined()) {
+ QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
+ setInitialProperties(v4, qmlContext, object, valuemap);
+ }
d->completeCreate();
@@ -1387,6 +1393,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
if (!valuemap->isUndefined())
r->d()->valuemap = valuemap;
+ r->d()->qmlContext = v4->qmlContext();
r->d()->parent = parent;
QQmlIncubator *incubator = r->d()->incubator.data();
@@ -1400,7 +1407,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
}
// XXX used by QSGLoader
-void QQmlComponentPrivate::initializeObjectWithInitialProperties(const QV4::Value &valuemap, QObject *toCreate)
+void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate)
{
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
QV4::ExecutionEngine *v4engine = QV8Engine::getV4(ep->v8engine());
@@ -1410,7 +1417,7 @@ void QQmlComponentPrivate::initializeObjectWithInitialProperties(const QV4::Valu
Q_ASSERT(object->as<QV4::Object>());
if (!valuemap.isUndefined())
- setInitialProperties(v4engine, object, valuemap);
+ setInitialProperties(v4engine, qmlContext, object, valuemap);
}
QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
@@ -1487,6 +1494,7 @@ QQmlComponentExtension::~QQmlComponentExtension()
QV4::Heap::QmlIncubatorObject::QmlIncubatorObject(QQmlIncubator::IncubationMode m)
: valuemap(QV4::Primitive::undefinedValue())
, statusChanged(QV4::Primitive::undefinedValue())
+ , qmlContext(0)
{
incubator.reset(new QQmlComponentIncubator(this, m));
}
@@ -1499,7 +1507,8 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o)
QV4::ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
- setInitialProperties(v4, obj, d()->valuemap);
+ QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
+ setInitialProperties(v4, qmlCtxt, obj, d()->valuemap);
}
}
@@ -1508,6 +1517,8 @@ void QV4::QmlIncubatorObject::markObjects(QV4::Heap::Base *that, QV4::ExecutionE
QmlIncubatorObject::Data *o = static_cast<QmlIncubatorObject::Data *>(that);
o->valuemap.mark(e);
o->statusChanged.mark(e);
+ if (o->qmlContext)
+ o->qmlContext->mark(e);
Object::markObjects(that, e);
}
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 15ec88dd52..ff6969fcdc 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -82,7 +82,7 @@ public:
QObject *beginCreate(QQmlContextData *);
void completeCreate();
- void initializeObjectWithInitialProperties(const QV4::Value &valuemap, QObject *toCreate);
+ void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate);
QQmlTypeData *typeData;
virtual void typeDataReady(QQmlTypeData *);
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index 456eedd0be..7cac79bd46 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -577,6 +577,7 @@ void QQuickLoader::setSource(QQmlV4Function *args)
d->disposeInitialPropertyValues();
d->initialPropertyValues.set(args->v4engine(), ipv);
}
+ d->qmlCallingContext.set(scope.engine, scope.engine->qmlContext());
setSource(sourceUrl, false); // already cleared and set ipv above.
}
@@ -645,7 +646,8 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj)
Q_ASSERT(v4);
QV4::Scope scope(v4);
QV4::ScopedValue ipv(scope, initialPropertyValues.value());
- d->initializeObjectWithInitialProperties(ipv, obj);
+ QV4::Scoped<QV4::QmlContext> qmlContext(scope, qmlCallingContext.value());
+ d->initializeObjectWithInitialProperties(qmlContext, ipv, obj);
}
void QQuickLoaderIncubator::statusChanged(Status status)
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 9677318b58..26a74be9d9 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -102,6 +102,7 @@ public:
QQmlContext *itemContext;
QQuickLoaderIncubator *incubator;
QV4::PersistentValue initialPropertyValues;
+ QV4::PersistentValue qmlCallingContext;
bool updatingSize: 1;
bool active : 1;
bool loadingFromSource : 1;
diff --git a/tests/auto/qml/qqmlcomponent/data/callingQmlContext.qml b/tests/auto/qml/qqmlcomponent/data/callingQmlContext.qml
new file mode 100644
index 0000000000..8193d0f36c
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/callingQmlContext.qml
@@ -0,0 +1,13 @@
+import QtQml 2.0
+import qqmlcomponenttest 1.0
+QtObject {
+ property Component factory
+ property QtObject incubatedObject
+
+ Component.onCompleted: {
+ var incubatorState = factory.incubateObject(null, { value: 42 })
+ incubatorState.onStatusChanged = function(status) {
+ incubatedObject = incubatorState.object
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlcomponent/data/callingQmlContextComponent.qml b/tests/auto/qml/qqmlcomponent/data/callingQmlContextComponent.qml
new file mode 100644
index 0000000000..adf491c87e
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/callingQmlContextComponent.qml
@@ -0,0 +1,3 @@
+import qqmlcomponenttest 1.0
+CallingContextCheckingClass {
+}
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 85579a6019..680ea720a8 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -40,6 +40,7 @@
#include <QtQuick>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
+#include <private/qv8engine_p.h>
#include <qcolor.h>
#include "../../shared/util.h"
#include "testhttpserver.h"
@@ -116,6 +117,7 @@ private slots:
void onDestructionCount();
void recursion();
void recursionContinuation();
+ void callingContextForInitialProperties();
private:
QQmlEngine engine;
@@ -523,6 +525,63 @@ void tst_qqmlcomponent::recursionContinuation()
QVERIFY(object->property("success").toBool());
}
+class CallingContextCheckingClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int value READ value WRITE setValue)
+public:
+ CallingContextCheckingClass()
+ : m_value(0)
+ {}
+
+ int value() const { return m_value; }
+ void setValue(int v) {
+ scopeObject.clear();
+ callingContextData.setContextData(0);
+
+ m_value = v;
+ QJSEngine *jsEngine = qjsEngine(this);
+ if (!jsEngine)
+ return;
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(jsEngine);
+ if (!v4)
+ return;
+ QV4::Scope scope(v4);
+ QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
+ if (!qmlContext)
+ return;
+ callingContextData = qmlContext->qmlContext();
+ scopeObject = qmlContext->qmlScope();
+ }
+
+ int m_value;
+ QQmlGuardedContextData callingContextData;
+ QPointer<QObject> scopeObject;
+};
+
+void tst_qqmlcomponent::callingContextForInitialProperties()
+{
+ qmlRegisterType<CallingContextCheckingClass>("qqmlcomponenttest", 1, 0, "CallingContextCheckingClass");
+
+ QQmlComponent testFactory(&engine, testFileUrl("callingQmlContextComponent.qml"));
+
+ QQmlComponent component(&engine, testFileUrl("callingQmlContext.qml"));
+ QScopedPointer<QObject> root(component.beginCreate(engine.rootContext()));
+ QVERIFY(!root.isNull());
+ root->setProperty("factory", QVariant::fromValue(&testFactory));
+ component.completeCreate();
+ QTRY_VERIFY(qvariant_cast<QObject *>(root->property("incubatedObject")));
+ QObject *o = qvariant_cast<QObject *>(root->property("incubatedObject"));
+ CallingContextCheckingClass *checker = qobject_cast<CallingContextCheckingClass*>(o);
+ QVERIFY(checker);
+
+ QVERIFY(!checker->callingContextData.isNull());
+ QVERIFY(checker->callingContextData->urlString().endsWith(QStringLiteral("callingQmlContext.qml")));
+
+ QVERIFY(!checker->scopeObject.isNull());
+ QVERIFY(checker->scopeObject->metaObject()->indexOfProperty("incubatedObject") != -1);
+}
+
QTEST_MAIN(tst_qqmlcomponent)
#include "tst_qqmlcomponent.moc"