QtQml: Empty SimpleArrayData vacant space when truncating

Without this we effectively soft-leak the contents of any
SimpleArrayData whenever we truncate it. Only when the array was either
completely dropped or re-filled would the extra objects be reclaimed.

Task-number: QTBUG-139025
Pick-to: 6.10 6.9 6.8
Change-Id: I88e9dc3ea8ec57c1de71b7b5417ebcfbaa75bb61
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2025-09-09 15:45:18 +02:00
parent 5a664f0836
commit e0f65fe66f
2 changed files with 37 additions and 2 deletions

View File

@ -264,14 +264,19 @@ uint SimpleArrayData::truncate(Object *o, uint newLen)
return newLen;
if (!dd->attrs) {
for (uint i = newLen; i < dd->values.size; ++i)
dd->setData(dd->internalClass->engine, i, Value::emptyValue());
dd->values.size = newLen;
return newLen;
}
while (dd->values.size > newLen) {
if (!dd->data(dd->values.size - 1).isEmpty() && !dd->attrs[dd->values.size - 1].isConfigurable())
const uint lastIndex = dd->values.size - 1;
if (!dd->data(lastIndex).isEmpty() && !dd->attrs[lastIndex].isConfigurable())
return dd->values.size;
--dd->values.size;
dd->setData(dd->internalClass->engine, lastIndex, Value::emptyValue());
dd->values.size = lastIndex;
}
return dd->values.size;
}

View File

@ -355,6 +355,7 @@ private slots:
#endif
void evalInGlobalContext();
void truncateArrayData();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
@ -6915,6 +6916,35 @@ void tst_QJSEngine::evalInGlobalContext()
QCOMPARE(ret.toString(), QLatin1String("99"));
}
void tst_QJSEngine::truncateArrayData()
{
QJSEngine engine;
QJSValue array = engine.newArray();
array.setProperty(0, QJSValue::NullValue);
array.setProperty(1, QJSValue(14));
array.setProperty(2, QJSValue(QLatin1String("aaa")));
// Append a JavaScript-owned object to the array and don't keep a local reference.
QJSValue object = engine.newQObject(new QObject());
QSignalSpy spy(object.toQObject(), &QObject::destroyed);
// std::move won't do here because setProperty() doesn't accept rvalue refs
array.setProperty(3, std::exchange(object, QJSValue()));
QVERIFY(object.isUndefined());
QCOMPARE(array.property("length").toInt(), 4);
gc(*engine.handle());
QCOMPARE(spy.count(), 0);
QCOMPARE(array.property("length").toInt(), 4);
// Truncating the array allows the GC to collect the QObject, which results in its deletion.
array.setProperty("length", 3);
QCOMPARE(array.property("length").toInt(), 3);
gc(*engine.handle());
QCOMPARE(spy.count(), 1);
}
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"