aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/particles/qquickitemparticle.cpp84
-rw-r--r--src/particles/qquickitemparticle_p.h3
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp8
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4engine.cpp99
-rw-r--r--src/qml/jsruntime/qv4engine_p.h11
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4object_p.h2
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp2
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp1
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp2
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp3
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp7
-rw-r--r--src/qml/jsruntime/qv4value_p.h2
-rw-r--r--src/qml/memory/qv4mm.cpp2
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp2
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp2
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp13
-rw-r--r--src/qmlmodels/qqmladaptormodel.cpp76
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp11
-rw-r--r--src/quick/configure.json2
-rw-r--r--src/quick/items/qquickitem_p.h2
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp2
-rw-r--r--tests/auto/particles/qquickitemparticle/data/loader.qml65
-rw-r--r--tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp37
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp182
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp64
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.h2
-rw-r--r--tests/auto/qml/qqmlapplicationengine/data/invalid.qml5
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp16
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp49
-rw-r--r--tests/auto/qml/qquickworkerscript/data/doRequest.mjs6
-rw-r--r--tests/auto/qml/qquickworkerscript/data/xmlHttpRequest.qml16
-rw-r--r--tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp8
-rw-r--r--tools/qml/qml.pro2
-rw-r--r--tools/qmlcachegen/qtquickcompiler.prf3
37 files changed, 700 insertions, 106 deletions
diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp
index 740d0f60f3..ce254c212d 100644
--- a/src/particles/qquickitemparticle.cpp
+++ b/src/particles/qquickitemparticle.cpp
@@ -136,6 +136,7 @@ QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) :
QQuickItemParticle::~QQuickItemParticle()
{
delete clock;
+ qDeleteAll(m_managed);
}
void QQuickItemParticle::freeze(QQuickItem* item)
@@ -172,7 +173,8 @@ void QQuickItemParticle::give(QQuickItem *item)
void QQuickItemParticle::initialize(int gIdx, int pIdx)
{
- m_loadables << m_system->groupData[gIdx]->data[pIdx];//defer to other thread
+ Q_UNUSED(gIdx);
+ Q_UNUSED(pIdx);
}
void QQuickItemParticle::commit(int, int)
@@ -205,50 +207,43 @@ void QQuickItemParticle::tick(int time)
{
Q_UNUSED(time);//only needed because QTickAnimationProxy expects one
processDeletables();
-
- foreach (QQuickParticleData* d, m_loadables){
- Q_ASSERT(d);
- if (m_stasis.contains(d->delegate))
- qWarning() << "Current model particles prefers overwrite:false";
- //remove old item from the particle that is dying to make room for this one
- if (d->delegate) {
- m_deletables << d->delegate;
- d->delegate = nullptr;
- }
- QQuickItem* parentItem = nullptr;
- if (!m_pendingItems.isEmpty()){
- QQuickItem *item = m_pendingItems.front();
- m_pendingItems.pop_front();
- parentItem = item->parentItem();
- d->delegate = item;
- }else if (m_delegate){
- d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this)));
- if (d->delegate)
- m_managed << d->delegate;
- }
- if (d && d->delegate){//###Data can be zero if creating an item leads to a reset - this screws things up.
- d->delegate->setX(d->curX(m_system) - d->delegate->width() / 2); //TODO: adjust for system?
- d->delegate->setY(d->curY(m_system) - d->delegate->height() / 2);
- QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate));
- if (mpa){
- mpa->m_parentItem = parentItem;
- mpa->m_mp = this;
- mpa->attach();
+ for (auto groupId : groupIds()) {
+ for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) {
+ if (!d->delegate && d->t != -1 && d->stillAlive(m_system)) {
+ QQuickItem* parentItem = nullptr;
+ if (!m_pendingItems.isEmpty()){
+ QQuickItem *item = m_pendingItems.front();
+ m_pendingItems.pop_front();
+ parentItem = item->parentItem();
+ d->delegate = item;
+ }else if (m_delegate){
+ d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this)));
+ if (d->delegate)
+ m_managed << d->delegate;
+ }
+ if (d && d->delegate){//###Data can be zero if creating an item leads to a reset - this screws things up.
+ d->delegate->setX(d->curX(m_system) - d->delegate->width() / 2); //TODO: adjust for system?
+ d->delegate->setY(d->curY(m_system) - d->delegate->height() / 2);
+ QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate));
+ if (mpa){
+ mpa->m_parentItem = parentItem;
+ mpa->m_mp = this;
+ mpa->attach();
+ }
+ d->delegate->setParentItem(this);
+ if (m_fade)
+ d->delegate->setOpacity(0.);
+ d->delegate->setVisible(false);//Will be set to true when we prepare the next frame
+ m_activeCount++;
+ }
}
- d->delegate->setParentItem(this);
- if (m_fade)
- d->delegate->setOpacity(0.);
- d->delegate->setVisible(false);//Will be set to true when we prepare the next frame
- m_activeCount++;
}
}
- m_loadables.clear();
}
void QQuickItemParticle::reset()
{
QQuickParticlePainter::reset();
- m_loadables.clear();
// delete all managed items which had their logical particles cleared
// but leave it alone if the logical particle is maintained
@@ -258,7 +253,7 @@ void QQuickItemParticle::reset()
lost.remove(d->delegate);
}
}
- m_deletables.append(lost.values());
+ m_deletables.unite(lost);
//TODO: This doesn't yet handle calling detach on taken particles in the system reset case
processDeletables();
}
@@ -267,18 +262,9 @@ void QQuickItemParticle::reset()
QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
{
//Dummy update just to get painting tick
- if (m_pleaseReset){
+ if (m_pleaseReset)
m_pleaseReset = false;
- //Refill loadables, delayed here so as to only happen once per frame max
- //### Constant resetting might lead to m_loadables never being populated when tick() occurs
- for (auto groupId : groupIds()) {
- for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) {
- if (!d->delegate && d->t != -1 && d->stillAlive(m_system)) {
- m_loadables << d;
- }
- }
- }
- }
+
prepareNextFrame();
update();//Get called again
diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h
index ea46c67db7..32c9881691 100644
--- a/src/particles/qquickitemparticle_p.h
+++ b/src/particles/qquickitemparticle_p.h
@@ -108,9 +108,8 @@ protected:
private:
void processDeletables();
void tick(int time = 0);
- QList<QQuickItem* > m_deletables;
+ QSet<QQuickItem* > m_deletables;
QList<QQuickItem* > m_managed;
- QList< QQuickParticleData* > m_loadables;
bool m_fade;
QList<QQuickItem*> m_pendingItems;
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 654d33b8d1..1c587d2367 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -586,7 +586,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n)
obj->arrayPut(oldSize, os->values.data() + os->offset, chunk);
toCopy -= chunk;
if (toCopy)
- obj->arrayPut(oldSize + chunk, os->values.data(), toCopy);
+ obj->setArrayLength(oldSize + chunk + toCopy);
}
return oldSize + n;
@@ -659,6 +659,12 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const
}
ScopedString p1s(scope, v1.toString(scope.engine));
ScopedString p2s(scope, v2.toString(scope.engine));
+
+ if (!p1s)
+ return false;
+ if (!p2s)
+ return true;
+
return p1s->toQString() < p2s->toQString();
}
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index d8d0d4739f..6c51b4cbcb 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -1048,8 +1048,9 @@ ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Val
}
}
+ ScopedValue val(scope);
while (k < len) {
- ScopedValue val(scope, instance->get(k));
+ val = instance->get(k);
if (val->sameValueZero(argv[0])) {
return Encode(true);
}
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index cd2327a45e..14eaa5bce8 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -43,6 +43,7 @@
#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/QTextStream>
+#include <QtCore/private/qvariant_p.h>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
@@ -118,6 +119,7 @@
#endif
#include <private/qv4sqlerrors_p.h>
#include <qqmlfile.h>
+#include <qmetatype.h>
#if USE(PTHREADS)
# include <pthread.h>
@@ -185,6 +187,91 @@ static void restoreJSValue(QDataStream &stream, void *data)
}
}
+struct JSArrayIterator {
+ QJSValue const* data;
+ quint32 index;
+};
+
+namespace {
+void createNewIteratorIfNonExisting(void **iterator) {
+ if (*iterator == nullptr)
+ *iterator = new JSArrayIterator;
+}
+}
+
+static QtMetaTypePrivate::QSequentialIterableImpl jsvalueToSequence (const QJSValue& value) {
+ using namespace QtMetaTypePrivate;
+
+ QSequentialIterableImpl iterator {};
+ if (!value.isArray()) {
+ // set up some functions so that non-array QSequentialIterables do not crash
+ // but instead appear as an empty sequence
+ iterator._size = [](const void *) {return 0;};
+ iterator._moveToBegin = [](const void *, void **) {};
+ iterator._moveToEnd = [](const void *, void **) {};
+ iterator._advance = [](void **, int) {};
+ iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/};
+ iterator._destroyIter = [](void **){};
+ return iterator;
+ }
+
+ iterator._iterable = &value;
+ iterator._iterator = nullptr;
+ iterator._metaType_id = qMetaTypeId<QVariant>();
+ iterator._metaType_flags = QVariantConstructionFlags::ShouldDeleteVariantData;
+ iterator._iteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability;
+ iterator._size = [](const void *p) -> int {
+ return static_cast<QJSValue const *>(p)->property(QString::fromLatin1("length")).toInt();
+ };
+ /* Lifetime management notes:
+ * _at and _get return a pointer to a JSValue allocated via QMetaType::create
+ * Because we set QVariantConstructionFlags::ShouldDeleteVariantData, QSequentialIterable::at
+ * and QSequentialIterable::operator*() will free that memory
+ */
+
+ iterator._at = [](const void *iterable, int index) -> void const * {
+ auto const value = static_cast<QJSValue const *>(iterable)->property(quint32(index)).toVariant();
+ return QMetaType::create(qMetaTypeId<QVariant>(), &value);
+ };
+ iterator._moveToBegin = [](const void *iterable, void **iterator) {
+ createNewIteratorIfNonExisting(iterator);
+ auto jsArrayIterator = static_cast<JSArrayIterator *>(*iterator);
+ jsArrayIterator->index = 0;
+ jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
+ };
+ iterator._moveToEnd = [](const void *iterable, void **iterator) {
+ createNewIteratorIfNonExisting(iterator);
+ auto jsArrayIterator = static_cast<JSArrayIterator *>(*iterator);
+ auto length = static_cast<QJSValue const *>(iterable)->property(QString::fromLatin1("length")).toInt();
+ jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
+ jsArrayIterator->index = quint32(length);
+ };
+ iterator._advance = [](void **iterator, int advanceBy) {
+ static_cast<JSArrayIterator *>(*iterator)->index += quint32(advanceBy);
+ };
+ iterator._get = []( void * const *iterator, int metaTypeId, uint flags) -> VariantData {
+ auto const * const arrayIterator = static_cast<const JSArrayIterator *>(*iterator);
+ QJSValue const * const jsArray = arrayIterator->data;
+ auto const value = jsArray->property(arrayIterator->index).toVariant();
+ Q_ASSERT(flags & QVariantConstructionFlags::ShouldDeleteVariantData);
+ return {metaTypeId, QMetaType::create(qMetaTypeId<QVariant>(), &value), flags};
+ };
+ iterator._destroyIter = [](void **iterator) {
+ delete static_cast<JSArrayIterator *>(*iterator);
+ };
+ iterator._equalIter = [](void * const *p, void * const *other) {
+ auto this_ = static_cast<const JSArrayIterator *>(*p);
+ auto that_ = static_cast<const JSArrayIterator *>(*other);
+ return this_->index == that_->index && this_->data == that_->data;
+ };
+ iterator._copyIter = [](void **iterator, void * const * otherIterator) {
+ auto *otherIter = (static_cast<JSArrayIterator const *>(*otherIterator));
+ static_cast<JSArrayIterator *>(*iterator)->index = otherIter->index;
+ static_cast<JSArrayIterator *>(*iterator)->data = otherIter->data;
+ };
+ return iterator;
+}
+
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
@@ -712,6 +799,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QtMetaTypePrivate::QSequentialIterableImpl>())
+ QMetaType::registerConverter<QJSValue, QtMetaTypePrivate::QSequentialIterableImpl>(jsvalueToSequence);
QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue);
QV4::QObjectWrapper::initializeBindings(this);
@@ -1178,8 +1267,11 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file)
void ExecutionEngine::markObjects(MarkStack *markStack)
{
for (int i = 0; i < NClasses; ++i)
- if (classes[i])
+ if (classes[i]) {
classes[i]->mark(markStack);
+ if (markStack->top >= markStack->limit)
+ markStack->drain();
+ }
markStack->drain();
identifierTable->markObjects(markStack);
@@ -2238,4 +2330,9 @@ int ExecutionEngine::registerExtension()
return registrationData()->extensionCount++;
}
+QNetworkAccessManager *QV4::detail::getNetworkAccessManager(ExecutionEngine *engine)
+{
+ return engine->qmlEngine()->networkAccessManager();
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index d233347060..e0025feb00 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -91,7 +91,14 @@ class PageAllocation;
QT_BEGIN_NAMESPACE
-namespace QV4 { struct QObjectMethod; }
+class QNetworkAccessManager;
+
+namespace QV4 {
+struct QObjectMethod;
+namespace detail {
+QNetworkAccessManager *getNetworkAccessManager(ExecutionEngine *engine);
+}
+}
// Used to allow a QObject method take and return raw V4 handles without having to expose
// 48 in the public API.
@@ -348,6 +355,8 @@ public:
FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); }
FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); }
+ QNetworkAccessManager* (*networkAccessManager)(ExecutionEngine*) = detail::getNetworkAccessManager;
+
enum JSStrings {
String_Empty,
String_undefined,
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 6fb7946023..dfef52583e 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -364,7 +364,13 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
if (!arr)
return v4->throwTypeError();
- uint len = arr->getLength();
+ const qint64 len64 = arr->getLength();
+ if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max()))
+ return v4->throwRangeError(QStringLiteral("Invalid array length."));
+ if (len64 > qint64(v4->jsStackLimit - v4->jsStackTop))
+ return v4->throwRangeError(QStringLiteral("Array too large for apply()."));
+
+ const uint len = uint(len64);
Scope scope(v4);
Value *arguments = scope.alloc<Scope::Uninitialized>(len);
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index f3375929a3..9e901d6a34 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -157,7 +157,7 @@ struct Q_QML_EXPORT Object: Managed {
const Value *propertyData(uint index) const { return d()->propertyData(index); }
Heap::ArrayData *arrayData() const { return d()->arrayData; }
- void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a->d()); }
+ void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a ? a->d() : nullptr); }
void getProperty(const InternalClassEntry &entry, Property *p) const;
void setProperty(const InternalClassEntry &entry, const Property *p);
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index e529b8e86b..65f6fa8b12 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -182,7 +182,7 @@ PropertyKey ForInIteratorObject::nextProperty() const
if (d()->current != d()->object) {
o = d()->object;
bool shadowed = false;
- while (o->d() != c->heapObject()) {
+ while (o && o->d() != c->heapObject()) {
if (o->getOwnProperty(key) != Attr_Invalid) {
shadowed = true;
break;
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 3d3b3f413f..7d910a1cbc 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -658,6 +658,7 @@ ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Va
ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
+ CHECK_STACK_LIMITS(scope.engine)
ScopedObject o(scope, thisObject->toObject(scope.engine));
if (!o)
RETURN_UNDEFINED();
diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp
index 9325e2e53b..51f96b9003 100644
--- a/src/qml/jsruntime/qv4proxy.cpp
+++ b/src/qml/jsruntime/qv4proxy.cpp
@@ -515,7 +515,7 @@ ProxyObjectOwnPropertyKeyIterator::ProxyObjectOwnPropertyKeyIterator(ArrayObject
PropertyKey ProxyObjectOwnPropertyKeyIterator::next(const Object *m, Property *pd, PropertyAttributes *attrs)
{
- if (index >= len)
+ if (index >= len || m == nullptr)
return PropertyKey::invalid();
Scope scope(m);
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp
index 4ed1dbd5aa..76daead842 100644
--- a/src/qml/jsruntime/qv4regexp.cpp
+++ b/src/qml/jsruntime/qv4regexp.cpp
@@ -73,14 +73,13 @@ DEFINE_MANAGED_VTABLE(RegExp);
uint RegExp::match(const QString &string, int start, uint *matchOffsets)
{
- static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1;
-
if (!isValid())
return JSC::Yarr::offsetNoMatch;
WTF::String s(string);
#if ENABLE(YARR_JIT)
+ static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1;
auto *priv = d();
if (priv->hasValidJITCode()) {
uint ret = JSC::Yarr::offsetNoMatch;
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index c1a42c4afa..f1375e4ca4 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -961,8 +961,8 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val
return scope.engine->throwTypeError();
Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc));
-
- r->d()->value.set(scope.engine, re->value());
+ if (re) // Otherwise the regexp constructor should have thrown an exception
+ r->d()->value.set(scope.engine, re->value());
return Encode::undefined();
}
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index 9c50ac2b95..2ed1971235 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -1409,7 +1409,8 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
if (scope.engine->hasException || l != len)
return scope.engine->throwTypeError();
- if (offset + l > a->length())
+ const uint aLength = a->length();
+ if (offset > aLength || l > aLength - offset)
RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
uint idx = 0;
@@ -1439,7 +1440,9 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
return scope.engine->throwTypeError();
uint l = srcTypedArray->length();
- if (offset + l > a->length())
+
+ const uint aLength = a->length();
+ if (offset > aLength || l > aLength - offset)
RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
char *dest = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize;
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 4e901721cb..ae12033eb4 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -555,6 +555,8 @@ struct ValueArray {
} else {
while (v < end) {
v->mark(markStack);
+ if (markStack->top >= markStack->limit)
+ markStack->drain();
++v;
}
}
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 3465036c86..35b2ff2749 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -1220,6 +1220,8 @@ void MemoryManager::collectFromJSStack(MarkStack *markStack) const
Q_ASSERT(m->inUse());
// Skip pointers to already freed objects, they are bogus as well
m->mark(markStack);
+ if (markStack->top >= markStack->limit)
+ markStack->drain();
}
++v;
}
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index adb036e2d0..7f80fe5e1c 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -127,7 +127,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
switch (c->status()) {
case QQmlComponent::Error:
qWarning() << "QQmlApplicationEngine failed to load component";
- qWarning() << qPrintable(c->errorString());
+ warning(c->errors());
q->objectCreated(nullptr, c->url());
break;
case QQmlComponent::Ready: {
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index df4639bb1d..c2e7be73e7 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -1651,7 +1651,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
Scope scope(f->engine());
const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f);
- QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->qmlEngine()->networkAccessManager(), scope.engine);
+ QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->networkAccessManager(scope.engine), scope.engine);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
w->setPrototypeUnchecked(proto);
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index d634a48443..f5e723419e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -1553,11 +1553,12 @@ static QString jsStack(QV4::ExecutionEngine *engine) {
return stack;
}
-static QString serializeArray(Object *array, ExecutionEngine *v4) {
+static QString serializeArray(Object *array, ExecutionEngine *v4, QSet<QV4::Heap::Object *> &alreadySeen) {
Scope scope(v4);
ScopedValue val(scope);
QString result;
+ alreadySeen.insert(array->d());
result += QLatin1Char('[');
const uint length = array->getLength();
for (uint i = 0; i < length; ++i) {
@@ -1565,12 +1566,15 @@ static QString serializeArray(Object *array, ExecutionEngine *v4) {
result += QLatin1Char(',');
val = array->get(i);
if (val->isManaged() && val->managed()->isArrayLike())
- result += serializeArray(val->objectValue(), v4);
+ if (!alreadySeen.contains(val->objectValue()->d()))
+ result += serializeArray(val->objectValue(), v4, alreadySeen);
+ else
+ result += QLatin1String("[Circular]");
else
result += val->toQStringNoThrow();
}
result += QLatin1Char(']');
-
+ alreadySeen.remove(array->d());
return result;
};
@@ -1600,8 +1604,9 @@ static ReturnedValue writeToConsole(const FunctionObject *b, const Value *, cons
if (i != start)
result.append(QLatin1Char(' '));
+ QSet<QV4::Heap::Object *> alreadySeenElements;
if (argv[i].isManaged() && argv[i].managed()->isArrayLike())
- result.append(serializeArray(argv[i].objectValue(), v4));
+ result.append(serializeArray(argv[i].objectValue(), v4, alreadySeenElements));
else
result.append(argv[i].toQStringNoThrow());
}
diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp
index 012540244f..cf0d8fbb2f 100644
--- a/src/qmlmodels/qqmladaptormodel.cpp
+++ b/src/qmlmodels/qqmladaptormodel.cpp
@@ -199,12 +199,11 @@ public:
RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model;
- if (o->d()->item->index >= 0 && *model) {
- const QAbstractItemModel * const aim = model->aim();
- RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
- } else {
- RETURN_RESULT(QV4::Encode(false));
+ if (o->d()->item->index >= 0) {
+ if (const QAbstractItemModel *const aim = model->aim())
+ RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
}
+ RETURN_RESULT(QV4::Encode(false));
}
@@ -400,23 +399,24 @@ public:
bool hasModelChildren() const
{
- if (index >= 0 && *type->model) {
- const QAbstractItemModel * const model = type->model->aim();
- return model->hasChildren(model->index(row, column, type->model->rootIndex));
- } else {
- return false;
+ if (index >= 0) {
+ if (const QAbstractItemModel *const model = type->model->aim())
+ return model->hasChildren(model->index(row, column, type->model->rootIndex));
}
+ return false;
}
QVariant value(int role) const override
{
- return type->model->aim()->index(row, column, type->model->rootIndex).data(role);
+ if (const QAbstractItemModel *aim = type->model->aim())
+ return aim->index(row, column, type->model->rootIndex).data(role);
+ return QVariant();
}
void setValue(int role, const QVariant &value) override
{
- type->model->aim()->setData(
- type->model->aim()->index(row, column, type->model->rootIndex), value, role);
+ if (QAbstractItemModel *aim = type->model->aim())
+ aim->setData(aim->index(row, column, type->model->rootIndex), value, role);
}
QV4::ReturnedValue get() override
@@ -444,12 +444,16 @@ public:
int rowCount(const QQmlAdaptorModel &model) const override
{
- return model.aim()->rowCount(model.rootIndex);
+ if (const QAbstractItemModel *aim = model.aim())
+ return aim->rowCount(model.rootIndex);
+ return 0;
}
int columnCount(const QQmlAdaptorModel &model) const override
{
- return model.aim()->columnCount(model.rootIndex);
+ if (const QAbstractItemModel *aim = model.aim())
+ return aim->columnCount(model.rootIndex);
+ return 0;
}
void cleanup(QQmlAdaptorModel &) const override
@@ -464,39 +468,46 @@ public:
dataType->initializeMetaType(model);
}
- QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8());
- if (it != roleNames.end()) {
- return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it);
- } else if (role == QLatin1String("hasModelChildren")) {
- return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)));
- } else {
- return QVariant();
+ if (const QAbstractItemModel *aim = model.aim()) {
+ QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8());
+ if (it != roleNames.end()) {
+ return aim->index(model.rowAt(index), model.columnAt(index),
+ model.rootIndex).data(*it);
+ } else if (role == QLatin1String("hasModelChildren")) {
+ return QVariant(aim->hasChildren(aim->index(model.rowAt(index),
+ model.columnAt(index),
+ model.rootIndex)));
+ }
}
+ return QVariant();
}
QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
{
- return model
- ? QVariant::fromValue(model.aim()->parent(model.rootIndex))
- : QVariant();
+ if (const QAbstractItemModel *aim = model.aim())
+ return QVariant::fromValue(aim->parent(model.rootIndex));
+ return QVariant();
}
QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
{
- return model
- ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))
- : QVariant();
+ if (const QAbstractItemModel *aim = model.aim())
+ return QVariant::fromValue(aim->index(model.rowAt(index), model.columnAt(index),
+ model.rootIndex));
+ return QVariant();
}
bool canFetchMore(const QQmlAdaptorModel &model) const override
{
- return model && model.aim()->canFetchMore(model.rootIndex);
+ if (const QAbstractItemModel *aim = model.aim())
+ return aim->canFetchMore(model.rootIndex);
+ return false;
}
void fetchMore(QQmlAdaptorModel &model) const override
{
- if (model)
- model.aim()->fetchMore(model.rootIndex);
+ if (QAbstractItemModel *aim = model.aim())
+ aim->fetchMore(model.rootIndex);
}
QQmlDelegateModelItem *createItem(
@@ -516,7 +527,8 @@ public:
setModelDataType<QQmlDMAbstractItemModelData>(&builder, this);
const QByteArray propertyType = QByteArrayLiteral("QVariant");
- const QHash<int, QByteArray> names = model.aim()->roleNames();
+ const QAbstractItemModel *aim = model.aim();
+ const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>();
for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) {
const int propertyId = propertyRoles.count();
propertyRoles.append(it.key());
diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
index 9e4b3e1b46..72ae6d5bd4 100644
--- a/src/qmlworkerscript/qquickworkerscript.cpp
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -130,6 +130,7 @@ struct WorkerScript : public QV4::ExecutionEngine {
QQuickWorkerScriptEnginePrivate *p = nullptr;
QUrl source;
QQuickWorkerScript *owner = nullptr;
+ QScopedPointer<QNetworkAccessManager> scriptLocalNAM;
int id = -1;
};
@@ -389,6 +390,16 @@ WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent)
QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(this, name, QQuickWorkerScriptEnginePrivate::method_sendMessage, 1));
api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage);
globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
+ networkAccessManager = [](QV4::ExecutionEngine *engine){
+ auto *workerScript = static_cast<WorkerScript *>(engine);
+ if (workerScript->scriptLocalNAM)
+ return workerScript->scriptLocalNAM.get();
+ if (auto *namFactory = workerScript->p->qmlengine->networkAccessManagerFactory())
+ workerScript->scriptLocalNAM.reset(namFactory->create(workerScript->p));
+ else
+ workerScript->scriptLocalNAM.reset(new QNetworkAccessManager(workerScript->p));
+ return workerScript->scriptLocalNAM.get();
+ };
}
int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
diff --git a/src/quick/configure.json b/src/quick/configure.json
index 0cb1e7470b..f8277332dc 100644
--- a/src/quick/configure.json
+++ b/src/quick/configure.json
@@ -3,9 +3,11 @@
"depends": [
"core-private",
"qml-private",
+ "gui",
"gui-private",
"qmlmodels-private"
],
+ "condition": "module.gui",
"testDir": "../../config.tests",
"commandline": {
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index bc0d04c763..baef37a0ff 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -533,7 +533,7 @@ public:
void refWindow(QQuickWindow *);
void derefWindow();
- QQuickItem *subFocusItem;
+ QPointer<QQuickItem> subFocusItem;
void updateSubFocusItem(QQuickItem *scope, bool focus);
QTransform windowToItemTransform() const;
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
index d0108bc56e..89c9ea4d5b 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -145,7 +145,9 @@ void QSGRhiTextureGlyphCache::prepareGlyphImage(QImage *img)
{
const int maskWidth = img->width();
const int maskHeight = img->height();
+#if Q_BYTE_ORDER != Q_BIG_ENDIAN
const bool supportsBgra = m_rhi->isTextureFormatSupported(QRhiTexture::BGRA8);
+#endif
m_bgra = false;
if (img->format() == QImage::Format_Mono) {
diff --git a/tests/auto/particles/qquickitemparticle/data/loader.qml b/tests/auto/particles/qquickitemparticle/data/loader.qml
new file mode 100644
index 0000000000..beac7a0410
--- /dev/null
+++ b/tests/auto/particles/qquickitemparticle/data/loader.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+Rectangle {
+ color: "black"
+ width: 320
+ height: 320
+
+ Component {
+ id: component
+
+ ParticleSystem {
+ id: sys
+ objectName: "system"
+ anchors.fill: parent
+ running: visible
+
+ ItemParticle {
+ delegate: Image { source: "../../shared/star.png" }
+ }
+
+ Emitter {
+ //0,0 position
+ size: 32
+ emitRate: 10
+ lifeSpan: 150000
+ }
+ }
+ }
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ sourceComponent: component
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
index 0087c74a9c..28ebbb3c05 100644
--- a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
+++ b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
@@ -46,6 +46,8 @@ private slots:
void test_deletion();
void test_noDeletion();
void test_takeGive();
+ void test_noCrashOnReset();
+ void test_noLeakWhenDeleted();
};
void tst_qquickitemparticle::initTestCase()
@@ -120,6 +122,41 @@ void tst_qquickitemparticle::test_takeGive()
delete view;
}
+void tst_qquickitemparticle::test_noCrashOnReset()
+{
+ QQuickView* view = createView(testFileUrl("basic.qml"), 600);
+ QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system");
+
+ for (int i = 0; i < 10; ++i) {
+ ensureAnimTime(16, system->m_animation);
+ system->reset();
+ }
+
+ delete view;
+}
+
+void tst_qquickitemparticle::test_noLeakWhenDeleted()
+{
+ QQuickView* view = createView(testFileUrl("loader.qml"), 500);
+ QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system");
+ ensureAnimTime(100, system->m_animation);
+
+ auto particles = qAsConst(system->groupData[0]->data);
+ QVERIFY(!particles.isEmpty());
+
+ QQuickParticleData* firstParticleData = particles.first();
+ QPointer<QQuickItem> firstParticleDelegate = firstParticleData->delegate;
+ QVERIFY(!firstParticleDelegate.isNull());
+
+ QQuickItem* loader = view->rootObject()->findChild<QQuickItem*>("loader");
+ loader->setProperty("active", false); //This should destroy the ParticleSystem, ItemParticle and Emitter
+
+ QTest::qWait(1); //Process events to make sure the loader is properly unloaded
+ QVERIFY(firstParticleDelegate.isNull()); //Delegates should be deleted
+
+ delete view;
+}
+
QTEST_MAIN(tst_qquickitemparticle);
#include "tst_qquickitemparticle.moc"
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index f1ff396d4f..66a526fda8 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -252,6 +252,17 @@ private slots:
void interrupt();
void triggerBackwardJumpWithDestructuring();
+ void arrayConcatOnSparseArray();
+ void sortSparseArray();
+ void compileBrokenRegexp();
+ void sortNonStringArray();
+ void iterateInvalidProxy();
+ void applyOnHugeArray();
+
+ void tostringRecursionCheck();
+ void arrayIncludesWithLargeArray();
+ void printCircularArray();
+ void typedArraySet();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
@@ -4961,6 +4972,177 @@ void tst_QJSEngine::triggerBackwardJumpWithDestructuring()
QVERIFY(!value.isError());
}
+void tst_QJSEngine::arrayConcatOnSparseArray()
+{
+ QJSEngine engine;
+ engine.installExtensions(QJSEngine::GarbageCollectionExtension);
+ const auto value = engine.evaluate(
+ "(function() {\n"
+ " const v4 = [1,2,3];\n"
+ " const v7 = [4,5];\n"
+ " v7.length = 1337;\n"
+ " const v9 = v4.concat(v7);\n"
+ " gc();\n"
+ " return v9;\n"
+ "})();");
+ QCOMPARE(value.property("length").toInt(), 1340);
+ for (int i = 0; i < 5; ++i)
+ QCOMPARE(value.property(i).toInt(), i + 1);
+ for (int i = 5; i < 1340; ++i)
+ QVERIFY(value.property(i).isUndefined());
+}
+
+void tst_QJSEngine::sortSparseArray()
+{
+ QJSEngine engine;
+ engine.installExtensions(QJSEngine::ConsoleExtension);
+ const auto value = engine.evaluate(
+ "(function() {\n"
+ " var sparse = [0];\n"
+ " sparse = Object.defineProperty(sparse, \"10\", "
+ " {get: ()=>{return 2}, set: ()=>{return 2}} );\n"
+ " return Array.prototype.sort.call(sparse, ()=>{});\n"
+ "})();");
+
+ QCOMPARE(value.property("length").toInt(), 11);
+ QVERIFY(value.property(0).isNumber());
+ QCOMPARE(value.property(0).toInt(), 0);
+ QVERIFY(value.property(1).isNumber());
+ QCOMPARE(value.property(1).toInt(), 2);
+ QVERIFY(value.property(10).isUndefined());
+}
+
+void tst_QJSEngine::compileBrokenRegexp()
+{
+ QJSEngine engine;
+ const auto value = engine.evaluate(
+ "(function() {"
+ "var ret = new RegExp(Array(4097).join("
+ " String.fromCharCode(58)) + Array(4097).join(String.fromCharCode(480)) "
+ " + Array(65537).join(String.fromCharCode(5307)));"
+ "return RegExp.prototype.compile.call(ret, 'a','b');"
+ "})();"
+ );
+
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), "SyntaxError: Invalid flags supplied to RegExp constructor");
+}
+
+void tst_QJSEngine::tostringRecursionCheck()
+{
+ QJSEngine engine;
+ auto value = engine.evaluate(R"js(
+ var a = {};
+ var b = new Array(1337);
+ function main() {
+ var ret = a.toLocaleString;
+ b[1] = ret;
+ Array = {};
+ Object.toString = b[1];
+ var ret = String.prototype.lastIndexOf.call({}, b[1]);
+ var ret = String.prototype.charAt.call(Function, Object);
+ }
+ main();
+ )js");
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), QLatin1String("RangeError: Maximum call stack size exceeded."));
+}
+
+void tst_QJSEngine::arrayIncludesWithLargeArray()
+{
+ QJSEngine engine;
+ auto value = engine.evaluate(R"js(
+ let arr = new Array(10000000)
+ arr.includes(42)
+ )js");
+ QVERIFY(value.isBool());
+ QCOMPARE(value.toBool(), false);
+}
+
+void tst_QJSEngine::printCircularArray()
+{
+ QJSEngine engine;
+ engine.installExtensions(QJSEngine::ConsoleExtension);
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg, "[[Circular]]");
+ auto value = engine.evaluate(R"js(
+ let v1 = []
+ v1.push(v1)
+ console.log(v1)
+ )js");
+}
+
+void tst_QJSEngine::sortNonStringArray()
+{
+ QJSEngine engine;
+ const auto value = engine.evaluate(
+ "const v4 = [Symbol.iterator, 1];"
+ "const v5 = v4.sort();"
+ );
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), "TypeError: Cannot convert a symbol to a string.");
+}
+
+void tst_QJSEngine::iterateInvalidProxy()
+{
+ QJSEngine engine;
+ const auto value = engine.evaluate(
+ "const v1 = new Proxy(Reflect, Reflect);"
+ "for (const v2 in v1) {}"
+ "const v3 = { getOwnPropertyDescriptor: eval, getPrototypeOf: eval };"
+ "const v4 = new Proxy(v3, v3);"
+ "for (const v5 in v4) {}"
+ );
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), "TypeError: Type error");
+}
+
+void tst_QJSEngine::applyOnHugeArray()
+{
+ QJSEngine engine;
+ const auto value = engine.evaluate(
+ "var a = new Array(10);"
+ "a[536870912] = Function;"
+ "Function.apply('aaaaaaaa', a);"
+ );
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), "RangeError: Array too large for apply().");
+}
+
+void tst_QJSEngine::typedArraySet()
+{
+ QJSEngine engine;
+ const auto value = engine.evaluate(
+ "(function() {"
+ " var length = 0xffffffe;"
+ " var offset = 0xfffffff0;"
+ " var e1;"
+ " var e2;"
+ " try {"
+ " var source1 = new Int8Array(length);"
+ " var target1 = new Int8Array(length);"
+ " target1.set(source1, offset);"
+ " } catch (intError) {"
+ " e1 = intError;"
+ " }"
+ " try {"
+ " var source2 = new Array(length);"
+ " var target2 = new Int8Array(length);"
+ " target2.set(source2, offset);"
+ " } catch (arrayError) {"
+ " e2 = arrayError;"
+ " }"
+ " return [e1, e2];"
+ "})();"
+ );
+
+ QVERIFY(value.isArray());
+ for (int i = 0; i < 2; ++i) {
+ const auto error = value.property(i);
+ QVERIFY(error.isError());
+ QCOMPARE(error.toString(), "RangeError: TypedArray.set: out of range");
+ }
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index 37d0ea4dea..95f554776f 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -2708,6 +2708,70 @@ void tst_QJSValue::nestedObjectToVariant()
QCOMPARE(o.toVariant(), expected);
}
+static int instanceCount = 0;
+
+struct MyType
+{
+ MyType(int n = 0, const char *t=nullptr): number(n), text(t)
+ {
+ ++instanceCount;
+ }
+ MyType(const MyType &other)
+ : number(other.number), text(other.text)
+ {
+ ++instanceCount;
+ }
+ ~MyType()
+ {
+ --instanceCount;
+ }
+ int number;
+ const char *text;
+};
+
+Q_DECLARE_METATYPE(MyType)
+Q_DECLARE_METATYPE(MyType*)
+
+void tst_QJSValue::jsvalueArrayToSequenceType()
+{
+ QCOMPARE(instanceCount, 0);
+ {
+ QJSEngine eng {};
+ auto testObject = eng.newObject();
+ testObject.setProperty("test", 42);
+ testObject.setProperty("mytypeobject", eng.toScriptValue(QVariant::fromValue(MyType {42, "hello"})));
+ auto array = eng.newArray(4);
+ array.setProperty(0, QLatin1String("Hello World"));
+ array.setProperty(1, 42);
+ array.setProperty(2, QJSValue(QJSValue::UndefinedValue));
+ array.setProperty(3, testObject);
+ auto asVariant = QVariant::fromValue(array);
+ QVERIFY(asVariant.canConvert<QVariantList>());
+ auto asIterable = asVariant.value<QSequentialIterable>();
+ for (auto it = asIterable.begin(); it != asIterable.end(); ++it) {
+ Q_UNUSED(*it)
+ }
+ int i = 0;
+ for (QVariant myVariant: asIterable) {
+ QCOMPARE(myVariant.isValid(), i != 2);
+ ++i;
+ }
+ QVERIFY(asIterable.at(2).value<QVariant>().isNull());
+ QCOMPARE(asIterable.at(3).value<QVariantMap>().find("mytypeobject")->value<MyType>().number, 42);
+ QCOMPARE(asIterable.at(0).value<QVariant>().toString(), QLatin1String("Hello World"));
+ auto it1 = asIterable.begin();
+ auto it2 = asIterable.begin();
+ QCOMPARE((*it1).value<QVariant>().toString(), (*it2).value<QVariant>().toString());
+ QCOMPARE((*it1).value<QVariant>().toString(), QLatin1String("Hello World"));
+ ++it2;
+ QCOMPARE((*it1).value<QVariant>().toString(), QLatin1String("Hello World"));
+ QCOMPARE((*it2).value<QVariant>().toInt(), 42);
+ }
+ // tests need to be done after engine has been destroyed, else it will hold a reference until
+ // the gc decides to collect it
+ QCOMPARE(instanceCount, 0);
+}
+
void tst_QJSValue::deleteFromDifferentThread()
{
#if !QT_CONFIG(thread)
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
index f704169d43..d85b9a0552 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
@@ -142,6 +142,8 @@ private slots:
void nestedObjectToVariant_data();
void nestedObjectToVariant();
+ void jsvalueArrayToSequenceType();
+
void deleteFromDifferentThread();
private:
diff --git a/tests/auto/qml/qqmlapplicationengine/data/invalid.qml b/tests/auto/qml/qqmlapplicationengine/data/invalid.qml
new file mode 100644
index 0000000000..5939a69a7a
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/data/invalid.qml
@@ -0,0 +1,5 @@
+import QtQml 2.12
+
+QtObject {
+ JUST_SOME_INVALID_PROPERTY: 0
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
index 0f5eea8b95..5e855efe1a 100644
--- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
@@ -30,6 +30,7 @@
#include <QQmlApplicationEngine>
#include <QScopedPointer>
#include <QSignalSpy>
+#include <QRegularExpression>
#if QT_CONFIG(process)
#include <QProcess>
#endif
@@ -53,6 +54,7 @@ private slots:
void loadTranslation_data();
void loadTranslation();
void setInitialProperties();
+ void failureToLoadTriggersWarningSignal();
private:
QString buildDir;
@@ -293,6 +295,20 @@ void tst_qqmlapplicationengine::setInitialProperties()
}
}
+Q_DECLARE_METATYPE(QList<QQmlError>) // for signalspy below
+
+void tst_qqmlapplicationengine::failureToLoadTriggersWarningSignal()
+{
+ auto url = testFileUrl("invalid.qml");
+ qRegisterMetaType<QList<QQmlError>>();
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlApplicationEngine failed to load component");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(url.toString() + QLatin1Char('*')));
+ QQmlApplicationEngine test;
+ QSignalSpy warningObserver(&test, &QQmlApplicationEngine::warnings);
+ test.load(url);
+ QTRY_COMPARE(warningObserver.count(), 1);
+}
+
QTEST_MAIN(tst_qqmlapplicationengine)
#include "tst_qqmlapplicationengine.moc"
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 46297b3c16..ec0db16114 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -381,6 +381,8 @@ private slots:
void semicolonAfterProperty();
void hugeStack();
+ void gcCrashRegressionTest();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(QQmlContextData *ctxt);
@@ -9213,6 +9215,53 @@ void tst_qqmlecmascript::hugeStack()
QCOMPARE(qvariant_cast<QJSValue>(huge).property(QLatin1String("length")).toInt(), 33059);
}
+void tst_qqmlecmascript::gcCrashRegressionTest()
+{
+ const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs";
+ if (!QFile::exists(qmljs)) {
+ QSKIP("Tets requires qmljs");
+ }
+ QProcess process;
+
+ QTemporaryFile infile;
+ QVERIFY(infile.open());
+ infile.write(R"js(
+ function i_want_to_break_free() {
+ var n = 400;
+ var m = 10;
+ var regex = new RegExp("(ab)".repeat(n), "g"); // g flag to trigger the vulnerable path
+ var part = "ab".repeat(n); // matches have to be at least size 2 to prevent interning
+ var s = (part + "|").repeat(m);
+ var cnt = 0;
+ var ary = [];
+ s.replace(regex, function() {
+ for (var i = 1; i < arguments.length-2; ++i) {
+ if (typeof arguments[i] !== 'string') {
+ i_am_free = arguments[i];
+ throw "success";
+ }
+ ary[cnt++] = arguments[i]; // root everything to force GC
+ }
+ return "x";
+ });
+ }
+ try { i_want_to_break_free(); } catch (e) {console.log("hi") }
+ console.log(typeof(i_am_free)); // will print "object"
+ )js");
+ infile.close();
+
+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
+ environment.insert("QV4_GC_MAX_STACK_SIZE", "32768");
+
+ process.setProcessEnvironment(environment);
+ process.start(qmljs, QStringList({infile.fileName()}));
+ QVERIFY(process.waitForStarted());
+ const qint64 pid = process.processId();
+ QVERIFY(pid != 0);
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitCode(), 0);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qquickworkerscript/data/doRequest.mjs b/tests/auto/qml/qquickworkerscript/data/doRequest.mjs
new file mode 100644
index 0000000000..d607c3400d
--- /dev/null
+++ b/tests/auto/qml/qquickworkerscript/data/doRequest.mjs
@@ -0,0 +1,6 @@
+WorkerScript.onMessage = function(message)
+{
+ var req = new XMLHttpRequest();
+ req.open("GET", message.url, true);
+ req.send();
+};
diff --git a/tests/auto/qml/qquickworkerscript/data/xmlHttpRequest.qml b/tests/auto/qml/qquickworkerscript/data/xmlHttpRequest.qml
new file mode 100644
index 0000000000..42136d78f0
--- /dev/null
+++ b/tests/auto/qml/qquickworkerscript/data/xmlHttpRequest.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.14
+
+Rectangle
+{
+ width: 100
+ height: 100
+
+ WorkerScript
+ {
+ source: "doRequest.mjs"
+ Component.onCompleted:
+ {
+ sendMessage({"url": "https://example.com"});
+ }
+ }
+}
diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
index bb4c9a7c1e..2f79f7157f 100644
--- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
+++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
@@ -60,6 +60,7 @@ private slots:
void script_function();
void script_var();
void stressDispose();
+ void xmlHttpRequest();
private:
void waitForEchoMessage(QQuickWorkerScript *worker) {
@@ -359,6 +360,13 @@ void tst_QQuickWorkerScript::stressDispose()
}
}
+void tst_QQuickWorkerScript::xmlHttpRequest()
+{
+ QQmlComponent component(&m_engine, testFileUrl("xmlHttpRequest.qml"));
+ QScopedPointer<QObject> root{component.create()}; // should not crash
+ QVERIFY(root);
+}
+
QTEST_MAIN(tst_QQuickWorkerScript)
#include "tst_qquickworkerscript.moc"
diff --git a/tools/qml/qml.pro b/tools/qml/qml.pro
index f086b7bff9..5dcbb3567a 100644
--- a/tools/qml/qml.pro
+++ b/tools/qml/qml.pro
@@ -8,7 +8,7 @@ RESOURCES += qml.qrc
QMAKE_TARGET_DESCRIPTION = QML Runtime
-ICON = resources/qml64.png
+ICON = resources/qml-64.png
win32 {
RC_ICONS = resources/qml.ico
}
diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf
index a31a7f5714..8aac3b9e07 100644
--- a/tools/qmlcachegen/qtquickcompiler.prf
+++ b/tools/qmlcachegen/qtquickcompiler.prf
@@ -27,7 +27,8 @@ defineReplace(qmlCacheResourceFileOutputName) {
}
# Flatten RESOURCES that may contain individual files or objects
-load(resources)
+load(resources_functions)
+qtFlattenResources()
NEWRESOURCES =
QMLCACHE_RESOURCE_FILES =