aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml')
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST3
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp6
-rw-r--r--tests/auto/qml/qqmlecmascript/BLACKLIST2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js29
-rw-r--r--tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp11
-rw-r--r--tests/auto/qml/qqmllanguage/data/ComponentType.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.15a.qml12
-rw-r--r--tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt3
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp63
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp14
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp89
15 files changed, 249 insertions, 28 deletions
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST
new file mode 100644
index 0000000000..3793debebb
--- /dev/null
+++ b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST
@@ -0,0 +1,3 @@
+# See qtbase/src/testlib/qtestblacklist.cpp for format
+[deleteChildrenWithRunningGroup]
+ci macos # QTBUG-106356
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 3c59f7bd56..773b826e62 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -346,7 +346,7 @@ void tst_qmldiskcache::regenerateAfterChange()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(1));
- QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script));
+ QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Script);
QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(0));
QCOMPARE(quint32(testUnit->functionTableSize), quint32(1));
@@ -374,7 +374,7 @@ void tst_qmldiskcache::regenerateAfterChange()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(2));
- QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number));
+ QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Number);
QCOMPARE(reinterpret_cast<const QV4::Value *>(testUnit->constants())
[obj->bindingTable()->value.constantValueIndex].doubleValue(),
@@ -419,7 +419,7 @@ void tst_qmldiskcache::registerImportForImplicitComponent()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(1));
- QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object));
+ QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Object);
const QV4::CompiledData::Object *implicitComponent = qmlUnit->objectAt(obj->bindingTable()->value.objectIndex);
QCOMPARE(testUnit->stringAtInternal(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType.elementName());
diff --git a/tests/auto/qml/qqmlecmascript/BLACKLIST b/tests/auto/qml/qqmlecmascript/BLACKLIST
deleted file mode 100644
index bd25566eef..0000000000
--- a/tests/auto/qml/qqmlecmascript/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[gcCrashRegressionTest]
-macos arm
diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js
new file mode 100644
index 0000000000..f51ab662ab
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js
@@ -0,0 +1,29 @@
+function init() {
+ Array.prototype.doPush = Array.prototype.push
+}
+
+function nasty() {
+ var sc_Vector = Array;
+ var push = sc_Vector.prototype.doPush;
+
+ // Change the memberData to hold something nasty on the current internalClass
+ sc_Vector.prototype.doPush = 5;
+
+ // Trigger a re-allocation of memberData
+ for (var i = 0; i < 256; ++i)
+ sc_Vector.prototype[i + "string"] = function() { return 98; }
+
+ // Change the (new) memberData back, to hold our doPush function again.
+ // This should propagate a protoId change all the way up to the lookup.
+ sc_Vector.prototype.doPush = push;
+}
+
+function func() {
+ var b = [];
+
+ // This becomes a lookup internally, which stores protoId and a pointer
+ // into the memberData. It should get invalidated when memberData is re-allocated.
+ b.doPush(3);
+
+ return b;
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml
new file mode 100644
index 0000000000..460c40a750
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml
@@ -0,0 +1,13 @@
+import QtQml 2.15
+
+import "internalClassParentGc.js" as Foo
+
+QtObject {
+ Component.onCompleted: {
+ gc();
+ Foo.init();
+ Foo.func();
+ Foo.nasty();
+ objectName = Foo.func()[0];
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 2291c31895..9d5ffda180 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -390,6 +390,8 @@ private slots:
void gcCrashRegressionTest();
void functionAsDefaultArgument();
+ void internalClassParentGc();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(QQmlContextData *ctxt);
@@ -9395,6 +9397,15 @@ void tst_qqmlecmascript::functionAsDefaultArgument()
QCOMPARE(root->objectName(), "didRun");
}
+void tst_qqmlecmascript::internalClassParentGc()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("internalClassParentGc.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QCOMPARE(root->objectName(), "3");
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmllanguage/data/ComponentType.qml b/tests/auto/qml/qqmllanguage/data/ComponentType.qml
new file mode 100644
index 0000000000..ad0dad455c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/ComponentType.qml
@@ -0,0 +1,8 @@
+import QtQml 2
+
+Component {
+ id: componentRoot
+ QtObject {
+ objectName: "enclosed"
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/alias.15a.qml b/tests/auto/qml/qqmllanguage/data/alias.15a.qml
new file mode 100644
index 0000000000..ba8097c997
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.15a.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+
+ property alias symbol: symbol
+ symbol.layer.enabled: true
+
+ Item {
+ id: symbol
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml
new file mode 100644
index 0000000000..7e10553ae7
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml
@@ -0,0 +1,11 @@
+import QtQuick 2
+
+Item {
+ id: root
+ Component {
+ id: accessibleNormal
+ Item {}
+ }
+ property alias accessibleNormalUrl: accessibleNormal.url
+ property url urlClone: root.accessibleNormalUrl // crashes qml utility
+}
diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml
new file mode 100644
index 0000000000..899b7aca37
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml
@@ -0,0 +1,11 @@
+import QtQuick 2
+Item {
+ id: root
+ Component {
+ id: accessibleNormal
+ ComponentType {
+ id: inaccessibleNormal
+ }
+ }
+ property alias accessibleNormalProgress: accessibleNormal.progress
+}
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
index e399799fe9..758be7feae 100644
--- a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
@@ -1,2 +1 @@
-2:8:Cannot assign to non-existent property "_G"
-
+2:11:Non-existent attached object
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 31a4135d89..df9d1401a9 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -174,7 +174,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::Execut
return;
}
- if (binding->type != QV4::CompiledData::Binding::Type_Script) {
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script) {
error(binding, QStringLiteral("Custom parser invoked with the wrong property value. Expected script that evaluates to enum"));
return;
}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index bffb62c59e..1be1533b63 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlprivate.h>
#include <QtQml/qqmlincubator.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfile.h>
@@ -338,6 +339,8 @@ private slots:
void hangOnWarning();
void ambiguousContainingType();
+ void staticConstexprMembers();
+ void bindingAliasToComponentUrl();
private:
QQmlEngine engine;
@@ -2146,6 +2149,22 @@ void tst_qqmllanguage::aliasProperties()
QCOMPARE(subItem->property("y").toInt(), 1);
}
+ // Nested property bindings on group properties that are actually aliases (QTBUG-94983)
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.15a.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QPointer<QObject> subItem = qvariant_cast<QObject*>(object->property("symbol"));
+ QVERIFY(!subItem.isNull());
+
+ QPointer<QObject> subSubItem = qvariant_cast<QObject*>(subItem->property("layer"));
+
+ QCOMPARE(subSubItem->property("enabled").value<bool>(), true);
+ }
+
// Alias to sub-object with binding (QTBUG-57041)
{
// This is shold *not* crash.
@@ -5875,6 +5894,50 @@ void tst_qqmllanguage::ambiguousContainingType()
QVERIFY(!o.isNull());
}
}
+ void tst_qqmllanguage::staticConstexprMembers() {
+ // this tests if the symbols are correclty defined for c++11 (gcc 11 and 12), and should
+ // not have any linker errors
+ using T = QObject;
+ using T2 = QObject;
+
+ auto f1 = QQmlPrivate::Constructors<T, true>::createSingletonInstance;
+ auto f2 = QQmlPrivate::Constructors<T, false>::createSingletonInstance;
+ auto f3 = QQmlPrivate::Constructors<T, true>::createInto;
+
+ auto f4 = QQmlPrivate::ExtendedType<T, true>::createParent;
+ auto f5 = QQmlPrivate::ExtendedType<T, false>::createParent;
+ auto f6 = QQmlPrivate::ExtendedType<T, true>::staticMetaObject;
+
+ auto f7 = QQmlPrivate::QmlSingleton<T, T2>::Value;
+
+ Q_UNUSED(f1);
+ Q_UNUSED(f2);
+ Q_UNUSED(f3);
+ Q_UNUSED(f3);
+ Q_UNUSED(f4);
+ Q_UNUSED(f5);
+ Q_UNUSED(f6);
+ Q_UNUSED(f7);
+ }
+
+void tst_qqmllanguage::bindingAliasToComponentUrl()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QCOMPARE(object->property("accessibleNormalUrl"), object->property("urlClone"));
+ }
+ {
+ QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl2.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QCOMPARE(object->property("accessibleNormalProgress"), QVariant(1.0));
+ }
+}
QTEST_MAIN(tst_qqmllanguage)
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index a75a00bd01..b6e0b00cc4 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -96,13 +96,13 @@ void tst_qqmltranslation::translation()
const bool expectCompiledTranslation = compiledTranslations.contains(propertyName);
if (expectCompiledTranslation) {
- if (binding->type != QV4::CompiledData::Binding::Type_Translation)
+ if (binding->type() != QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
- QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation));
+ QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_Translation);
} else {
- if (binding->type == QV4::CompiledData::Binding::Type_Translation)
+ if (binding->type() == QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation";
- QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation);
+ QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation);
}
}
}
@@ -148,11 +148,11 @@ void tst_qqmltranslation::idTranslation()
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
if (propertyName == "idTranslation") {
- if (binding->type != QV4::CompiledData::Binding::Type_TranslationById)
+ if (binding->type() != QV4::CompiledData::Binding::Type_TranslationById)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
- QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById));
+ QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_TranslationById);
} else {
- QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation);
+ QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation);
}
}
}
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index 824fd89e5b..34da3a7c50 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -47,7 +47,7 @@ private slots:
void gcStats();
void multiWrappedQObjects();
void accessParentOnDestruction();
- void clearICParent();
+ void cleanInternalClasses();
};
void tst_qv4mm::gcStats()
@@ -110,16 +110,41 @@ void tst_qv4mm::accessParentOnDestruction()
QCOMPARE(obj->property("destructions").toInt(), 100);
}
-void tst_qv4mm::clearICParent()
+void tst_qv4mm::cleanInternalClasses()
{
QV4::ExecutionEngine engine;
QV4::Scope scope(engine.rootContext());
QV4::ScopedObject object(scope, engine.newObject());
+ QV4::ScopedObject prototype(scope, engine.newObject());
+
+ // Set a prototype so that we get a unique IC.
+ object->setPrototypeOf(prototype);
+
+ QV4::Scoped<QV4::InternalClass> prevIC(scope, object->internalClass());
+ QVERIFY(prevIC->d()->transitions.empty());
+
+ uint prevIcChainLength = 0;
+ for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent)
+ ++prevIcChainLength;
+
+ const auto checkICCHainLength = [&]() {
+ uint icChainLength = 0;
+ for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent)
+ ++icChainLength;
+
+ const uint redundant = object->internalClass()->numRedundantTransitions;
+ QVERIFY(redundant <= QV4::Heap::InternalClass::MaxRedundantTransitions);
+
+ // A removal makes two transitions redundant.
+ QVERIFY(icChainLength <= prevIcChainLength + 2 * redundant);
+ };
+
+ const uint numTransitions = 16 * 1024;
// Keep identifiers in a separate array so that we don't have to allocate them in the loop that
// should test the GC on InternalClass allocations.
QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject());
- for (uint i = 0; i < 16 * 1024; ++i) {
+ for (uint i = 0; i < numTransitions; ++i) {
QV4::Scope scope(&engine);
QV4::ScopedString s(scope);
s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i));
@@ -130,22 +155,60 @@ void tst_qv4mm::clearICParent()
object->insertMember(s, v);
}
- // When allocating the InternalClass objects required for deleting properties, the GC should
- // eventually run and remove all but the last two.
- // If we ever manage to avoid allocating the InternalClasses in the first place we will need
- // to change this test.
- for (uint i = 0; i < 16 * 1024; ++i) {
+ // There is a chain of ICs originating from the original class.
+ QCOMPARE(prevIC->d()->transitions.size(), 1u);
+ QVERIFY(prevIC->d()->transitions.front().lookup != nullptr);
+
+ // When allocating the InternalClass objects required for deleting properties, eventually
+ // the IC chain gets truncated, dropping all the removed properties.
+ for (uint i = 0; i < numTransitions; ++i) {
QV4::Scope scope(&engine);
QV4::ScopedString s(scope, identifiers->get(i));
QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass());
QVERIFY(ic->d()->parent != nullptr);
- object->deleteProperty(s->toPropertyKey());
+ QV4::ScopedValue val(scope, object->get(s->toPropertyKey()));
+ QCOMPARE(val->toNumber(), double(i));
+ QVERIFY(object->deleteProperty(s->toPropertyKey()));
+ QVERIFY(!object->hasProperty(s->toPropertyKey()));
QVERIFY(object->internalClass() != ic->d());
- QCOMPARE(object->internalClass()->parent, ic->d());
- if (ic->d()->parent == nullptr)
- return;
}
- QFAIL("Garbage collector was not triggered by large amount of InternalClasses");
+
+ // None of the properties we've added are left
+ for (uint i = 0; i < numTransitions; ++i) {
+ QV4::ScopedString s(scope, identifiers->get(i));
+ QVERIFY(!object->hasProperty(s->toPropertyKey()));
+ }
+
+ // Also no other properties have appeared
+ QScopedPointer<QV4::OwnPropertyKeyIterator> iterator(object->ownPropertyKeys(object));
+ QVERIFY(!iterator->next(object).isValid());
+
+ checkICCHainLength();
+
+ // Add and remove properties until it clears all remaining redundant ones
+ uint i = 0;
+ while (object->internalClass()->numRedundantTransitions > 0) {
+ i = (i + 1) % numTransitions;
+ QV4::ScopedString s(scope, identifiers->get(i));
+ QV4::ScopedValue v(scope);
+ v->setDouble(i);
+ object->insertMember(s, v);
+ QVERIFY(object->deleteProperty(s->toPropertyKey()));
+ }
+
+ // Make sure that all dangling ICs are actually gone.
+ scope.engine->memoryManager->runGC();
+
+ // Now the GC has removed the ICs we originally added by adding properties.
+ QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr);
+
+ // Same thing with redundant prototypes
+ for (uint i = 0; i < numTransitions; ++i) {
+ QV4::ScopedObject prototype(scope, engine.newObject());
+ object->setPrototypeOf(prototype); // Makes previous prototype redundant
+ }
+
+ checkICCHainLength();
}
QTEST_MAIN(tst_qv4mm)