QJsonObject: add missing detach2() calls

The refactoring to use CBOR missed two places where we could assign from
the same object and thus cause corruption. In fixing this issue, I found
a design flaw in QJsonObject, see Q_EXPECT_FAILing unit test and task
QTBUG-83398.

[ChangeLog][QtCore][QJsonObject] Fixed a regression from 5.13 that
incorrect results when assigning elements from an object to itself.

Fixes: QTBUG-83366
Change-Id: Ibdc95e9af7bd456a94ecfffd1603df24b06713aa
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
bb10
Thiago Macieira 2020-04-08 11:40:42 -03:00
parent 1b7de8988c
commit ddc7b3c156
2 changed files with 100 additions and 0 deletions

View File

@ -452,9 +452,11 @@ QJsonValueRef QJsonObject::atImpl(T key)
bool keyExists = false;
int index = indexOf(o, key, &keyExists);
if (!keyExists) {
detach2(o->elements.length() / 2 + 1);
o->insertAt(index, key);
o->insertAt(index + 1, QCborValue::fromJsonValue(QJsonValue()));
}
// detaching will happen if and when this QJsonValueRef is assigned to
return QJsonValueRef(this, index / 2);
}
@ -1469,6 +1471,7 @@ QJsonValue QJsonObject::valueAt(int i) const
void QJsonObject::setValueAt(int i, const QJsonValue &val)
{
Q_ASSERT(o && i >= 0 && 2 * i + 1 < o->elements.length());
detach2();
if (val.isUndefined()) {
o->removeAt(2 * i + 1);
o->removeAt(2 * i);

View File

@ -54,7 +54,9 @@ private Q_SLOTS:
void testObjectSimple();
void testObjectSmallKeys();
void testObjectInsertCopies();
void testArraySimple();
void testArrayInsertCopies();
void testValueObject();
void testValueArray();
void testObjectNested();
@ -531,6 +533,75 @@ void tst_QtJson::testObjectSmallKeys()
QCOMPARE(data1.end() - data1.begin(), 3);
}
void tst_QtJson::testObjectInsertCopies()
{
{
QJsonObject obj;
obj["prop1"] = "TEST";
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("prop1"), "TEST");
obj["prop2"] = obj.value("prop1");
QCOMPARE(obj.size(), 2);
QCOMPARE(obj.value("prop1"), "TEST");
QCOMPARE(obj.value("prop2"), "TEST");
}
{
// see QTBUG-83366
QJsonObject obj;
obj["value"] = "TEST";
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("value"), "TEST");
obj["prop2"] = obj.value("value");
QCOMPARE(obj.size(), 2);
QCOMPARE(obj.value("value"), "TEST");
QCOMPARE(obj.value("prop2"), "TEST");
}
{
QJsonObject obj;
obj["value"] = "TEST";
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("value"), "TEST");
// same as previous, but this is a QJsonValueRef
QJsonValueRef rv = obj["prop2"];
rv = obj["value"];
QCOMPARE(obj.size(), 2);
QCOMPARE(obj.value("value"), "TEST");
QCOMPARE(obj.value("prop2"), "TEST");
}
{
QJsonObject obj;
obj["value"] = "TEST";
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("value"), "TEST");
// same as previous, but this is a QJsonValueRef
QJsonValueRef rv = obj["value"];
obj["prop2"] = rv;
QCOMPARE(obj.size(), 2);
QCOMPARE(obj.value("value"), "TEST");
QEXPECT_FAIL("", "QTBUG-83398: design flaw: the obj[] call invalidates the QJsonValueRef", Continue);
QCOMPARE(obj.value("prop2"), "TEST");
}
{
QJsonObject obj;
obj["value"] = "TEST";
QCOMPARE(obj.size(), 1);
QCOMPARE(obj.value("value"), "TEST");
QJsonValueRef v = obj["value"];
QJsonObject obj2 = obj;
obj.insert("prop2", v);
QCOMPARE(obj.size(), 2);
QCOMPARE(obj.value("value"), "TEST");
QCOMPARE(obj.value("prop2"), "TEST");
QCOMPARE(obj2.size(), 1);
QCOMPARE(obj2.value("value"), "TEST");
}
}
void tst_QtJson::testArraySimple()
{
QJsonArray array;
@ -584,6 +655,32 @@ void tst_QtJson::testArraySimple()
QCOMPARE(array.at(1), QJsonValue(QLatin1String("test")));
}
void tst_QtJson::testArrayInsertCopies()
{
{
QJsonArray array;
array.append("TEST");
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0), "TEST");
array.append(array.at(0));
QCOMPARE(array.size(), 2);
QCOMPARE(array.at(0), "TEST");
QCOMPARE(array.at(1), "TEST");
}
{
QJsonArray array;
array.append("TEST");
QCOMPARE(array.size(), 1);
QCOMPARE(array.at(0), "TEST");
array.prepend(array.at(0));
QCOMPARE(array.size(), 2);
QCOMPARE(array.at(0), "TEST");
QCOMPARE(array.at(1), "TEST");
}
}
void tst_QtJson::testValueObject()
{
QJsonObject object;