aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp11
-rw-r--r--tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp58
2 files changed, 68 insertions, 1 deletions
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 0412695404..7124a0a949 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -265,6 +265,8 @@ int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::
break;
}
+ bool shiftWithinBucket = false;
+
for (int i = 0; i < alloc; ++i) {
int idx = (i + start) % alloc;
Heap::StringOrSymbol *entry = table[idx];
@@ -273,7 +275,7 @@ int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::
continue;
}
if (entry->isMarked()) {
- if (lastEntry >= 0 && lastKey == (f(entry) % alloc)) {
+ if (lastEntry >= 0 && ((lastKey == f(entry) % alloc) || shiftWithinBucket)) {
Q_ASSERT(table[lastEntry] == nullptr);
table[lastEntry] = entry;
table[idx] = nullptr;
@@ -282,9 +284,16 @@ int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::
do {
lastEntry = (lastEntry + 1) % alloc;
} while (table[lastEntry] != nullptr);
+
+ // Avoid gaps within a bucket by enabling shift mode
+ // if we've caught up to the current index.
+ shiftWithinBucket = lastEntry == idx;
}
continue;
}
+
+ shiftWithinBucket = false;
+
if (lastEntry == -1) {
lastEntry = idx;
lastKey = f(entry) % alloc;
diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
index b2908ac5bb..095943cdc7 100644
--- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
+++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
@@ -41,6 +41,7 @@ private slots:
void sweepFirstEntryInSameBucketWithDifferingHash();
void dontSweepAcrossBucketBoundaries();
void sweepAcrossBucketBoundariesIfFirstBucketFull();
+ void sweepBucketGap();
};
void tst_qv4identifiertable::sweepFirstEntryInBucket()
@@ -300,6 +301,63 @@ void tst_qv4identifiertable::sweepAcrossBucketBoundariesIfFirstBucketFull()
QCOMPARE(table.entriesByHash[3], nullptr);
}
+void tst_qv4identifiertable::sweepBucketGap()
+{
+ QV4::ExecutionEngine engine;
+ QV4::IdentifierTable table(&engine, /*numBits*/3);
+
+ auto entry1 = engine.newString(QStringLiteral("one"));
+ auto entry2 = engine.newString(QStringLiteral("two"));
+ auto entry3 = engine.newString(QStringLiteral("three"));
+ auto entry4 = engine.newString(QStringLiteral("four"));
+
+ entry1->createHashValue();
+ entry2->createHashValue();
+ entry3->createHashValue();
+ entry4->createHashValue();
+
+ // We have two buckets where the second entry in the first bucket
+ // flows into the second bucket. So insertion into the second bucket
+ // will shift by one and create
+ // [entry1][entry2 (would map to first but overflows into second), entry3, entry4]
+ // Garbage collecting the first entry should not only move the second entry
+ // into its own first bucket (where there is space now), it is also critical to
+ // not leave a gap but move the third and fourth entries to the beginning of
+ // their bucket:
+ // [entry2][entry3, entry4]
+ entry1->stringHash = 0; // % 11 -> 0
+ entry2->stringHash = 11; // % 11 -> 0, but ends up at idx 1 because 0 taken
+ entry3->stringHash = 12; // % 11 -> 1, but ends up at idx 2 because 1 taken
+ entry4->stringHash = 12; // % 11 -> 1, but ends up at idx 3 because 1+2 taken
+
+ // trigger insertion
+ table.asPropertyKey(entry1);
+ table.asPropertyKey(entry2);
+ table.asPropertyKey(entry3);
+ table.asPropertyKey(entry4);
+
+ QCOMPARE(table.size, 4);
+ QCOMPARE(table.alloc, 11);
+
+ QCOMPARE(table.entriesByHash[0], entry1);
+ QCOMPARE(table.entriesByHash[1], entry2);
+ QCOMPARE(table.entriesByHash[2], entry3);
+ QCOMPARE(table.entriesByHash[3], entry4);
+ QCOMPARE(table.entriesByHash[4], nullptr);
+
+ // first entry not marked
+ entry2->setMarkBit();
+ entry3->setMarkBit();
+ entry4->setMarkBit();
+
+ table.sweep();
+
+ QCOMPARE(table.entriesByHash[0], entry2);
+ QCOMPARE(table.entriesByHash[1], entry3);
+ QCOMPARE(table.entriesByHash[2], entry4);
+ QCOMPARE(table.entriesByHash[3], nullptr);
+}
+
QTEST_MAIN(tst_qv4identifiertable)
#include "tst_qv4identifiertable.moc"