diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp | 2 | ||||
-rw-r--r-- | src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4global_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4identifier.cpp | 48 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4identifier_p.h | 35 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4identifiertable.cpp | 75 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4identifiertable_p.h | 20 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4internalclass.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4internalclass_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4object.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4string.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4string_p.h | 9 | ||||
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8engine.cpp | 2 |
16 files changed, 178 insertions, 55 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 9ed96d560f..cc1db7b5ce 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -272,7 +272,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) QV4::ScopedValue v(scope); QV4::Heap::InternalClass *ic = ctxt->internalClass(); for (uint i = 0; i < ic->size; ++i) { - QString name = scope.engine->identifierTable->stringForId(ic->nameMap[i])->toQString(); + QString name = ic->nameMap[i].toQString(); names.append(name); v = ctxt->d()->locals[i]; collectedRefs.append(collect(v)); diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index 0f064d8371..298608b63e 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -497,7 +497,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a QV4::Heap::InternalClass *ic = callContext->internalClass(); QV4::ScopedValue v(scope); for (uint i = 0; i < ic->size; ++i) { - QString name = scope.engine->identifierTable->stringForId(ic->nameMap[i])->toQString(); + QString name = ic->nameMap[i].toQString(); v = callContext->d()->locals[i]; collector.collect(&output, QString(), name, v); } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 057a437db7..af752cf243 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -919,8 +919,6 @@ void ExecutionEngine::requireArgumentsAccessors(int n) void ExecutionEngine::markObjects(MarkStack *markStack) { - identifierTable->mark(markStack); - for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) @@ -934,6 +932,8 @@ void ExecutionEngine::markObjects(MarkStack *markStack) classes[i]->mark(markStack); markStack->drain(); + identifierTable->markObjects(markStack); + for (auto compilationUnit: compilationUnits) { compilationUnit->markObjects(markStack); markStack->drain(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 69c62261d2..4316967484 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -157,11 +157,6 @@ public: QV8Engine *v8Engine; QJSEngine *publicEngine; -private: - quintptr currentIdentifier = 0; -public: - Identifier nextIdentifier() { return Identifier{ ++currentIdentifier }; } - enum JSObjects { RootContext, IntegerNull, // Has to come after the RootContext to make the context stack safe diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index d9660869f4..6820042406 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -252,6 +252,7 @@ typedef Scoped<ExecutionContext> ScopedContext; struct PersistentValueStorage; class PersistentValue; class WeakValue; +struct MarkStack; struct IdentifierTable; class RegExpCache; diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index 0bde74b718..1be24917db 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -44,6 +44,16 @@ QT_BEGIN_NAMESPACE namespace QV4 { +QString Identifier::toQString() const +{ + if (isArrayIndex()) + return QString::number(asArrayIndex()); + Heap::Base *b = asHeapObject(); + Q_ASSERT(b->internalClass->vtable->isStringOrSymbol); + Heap::StringOrSymbol *s = static_cast<Heap::StringOrSymbol *>(b); + return s->toQString(); +} + static const uchar prime_deltas[] = { 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 @@ -55,14 +65,16 @@ static inline int primeForNumBits(int numBits) } -IdentifierHashData::IdentifierHashData(int numBits) +IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) : size(0) , numBits(numBits) + , identifierTable(table) { refCount.store(1); alloc = primeForNumBits(numBits); entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); } IdentifierHashData::IdentifierHashData(IdentifierHashData *other) @@ -74,12 +86,18 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other) alloc = other->alloc; entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); +} + +IdentifierHashData::~IdentifierHashData() { + free(entries); + if (identifierTable) + identifierTable->removeIdentifierHash(this); } IdentifierHash::IdentifierHash(ExecutionEngine *engine) { - d = new IdentifierHashData(3); - d->identifierTable = engine->identifierTable; + d = new IdentifierHashData(engine->identifierTable, 3); } void IdentifierHash::detach() @@ -95,6 +113,8 @@ void IdentifierHash::detach() IdentifierHashEntry *IdentifierHash::addEntry(Identifier identifier) { + Q_ASSERT(identifier.isValid()); + // fill up to max 50% bool grow = (d->alloc <= d->size*2); @@ -105,7 +125,7 @@ IdentifierHashEntry *IdentifierHash::addEntry(Identifier identifier) memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); for (int i = 0; i < d->alloc; ++i) { const IdentifierHashEntry &e = d->entries[i]; - if (!e.identifier) + if (!e.identifier.isValid()) continue; uint idx = e.identifier.id % newAlloc; while (newEntries[idx].identifier.isValid()) { @@ -132,13 +152,13 @@ IdentifierHashEntry *IdentifierHash::addEntry(Identifier identifier) const IdentifierHashEntry *IdentifierHash::lookup(Identifier identifier) const { - if (!d) + if (!d || !identifier.isValid()) return nullptr; Q_ASSERT(d->entries); uint idx = identifier.id % d->alloc; while (1) { - if (!d->entries[idx].identifier) + if (!d->entries[idx].identifier.isValid()) return nullptr; if (d->entries[idx].identifier == identifier) return d->entries + idx; @@ -160,8 +180,9 @@ const IdentifierHashEntry *IdentifierHash::lookup(String *str) const { if (!d) return nullptr; - if (str->d()->identifier.isValid()) - return lookup(str->d()->identifier); + Identifier id = d->identifierTable->identifier(str); + if (id.isValid()) + return lookup(id); return lookup(str->toQString()); } @@ -189,6 +210,17 @@ QString QV4::IdentifierHash::findId(int value) const return QString(); } +void IdentifierHashData::markObjects(MarkStack *markStack) const +{ + IdentifierHashEntry *e = entries; + IdentifierHashEntry *end = e + alloc; + while (e < end) { + if (Heap::Base *o = e->identifier.asHeapObject()) + o->mark(markStack); + ++e; + } +} + } diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index 660cc2db05..b167a149a2 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -51,26 +51,30 @@ // #include <qstring.h> +#include <private/qv4global_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -namespace Heap { - struct String; -} - -struct String; -struct IdentifierTable; -struct ExecutionEngine; - struct Identifier { - quintptr id; + // id's are either pointers to Heap::String or Heap::Symbol from the Identifier table. + // For Symbol is can simply point to itself. + // This gives us automative GC'ing of identifiers + // In addition, an identifier can have the lowest bit set, and then indicates an array index + quint64 id; + + static Identifier invalid() { return Identifier{ 0 }; } + static Identifier fromArrayIndex(uint idx) { return Identifier{ (quint64(idx) << 1) | 1 }; } + bool isValid() const { return id && !(id & 1); } + uint asArrayIndex() const { return (id & 1) ? (id >> 1) : std::numeric_limits<uint>::max(); } + uint isArrayIndex() const { return (id & 1); } + static Identifier fromHeapObject(Heap::Base *b) { return Identifier{ reinterpret_cast<quintptr>(b) }; } + Heap::Base *asHeapObject() const { return (id & 1) ? nullptr : reinterpret_cast<Heap::Base *>(id); } + + Q_QML_EXPORT QString toQString() const; - static Identifier invalid() { return Identifier{0}; } - bool isValid() const { return id != 0; } - bool operator !() const { return id == 0; } bool operator ==(const Identifier &other) const { return id == other.id; } bool operator !=(const Identifier &other) const { return id != other.id; } bool operator <(const Identifier &other) const { return id < other.id; } @@ -84,11 +88,10 @@ struct IdentifierHashEntry { struct IdentifierHashData { - IdentifierHashData(int numBits); + IdentifierHashData(IdentifierTable *table, int numBits); explicit IdentifierHashData(IdentifierHashData *other); - ~IdentifierHashData() { - free(entries); - } + ~IdentifierHashData(); + void markObjects(MarkStack *markStack) const; QBasicAtomicInt refCount; int alloc; diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 2cfdd81c87..46c631476a 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -69,6 +69,8 @@ IdentifierTable::~IdentifierTable() { free(entriesByHash); free(entriesById); + for (auto &h : idHashes) + h->identifierTable = nullptr; } void IdentifierTable::addEntry(Heap::String *str) @@ -78,7 +80,7 @@ void IdentifierTable::addEntry(Heap::String *str) if (str->subtype == Heap::String::StringType_ArrayIndex) return; - str->identifier = engine->nextIdentifier(); + str->identifier = Identifier::fromHeapObject(str); bool grow = (alloc <= size*2); @@ -164,8 +166,10 @@ Identifier IdentifierTable::identifierImpl(const Heap::String *str) if (str->identifier.isValid()) return str->identifier; uint hash = str->hashValue(); - if (str->subtype == Heap::String::StringType_ArrayIndex) - return Identifier::invalid(); + if (str->subtype == Heap::String::StringType_ArrayIndex) { + str->identifier = Identifier::fromArrayIndex(hash); + return str->identifier; + } uint idx = hash % alloc; while (Heap::String *e = entriesByHash[idx]) { @@ -183,7 +187,10 @@ Identifier IdentifierTable::identifierImpl(const Heap::String *str) Heap::String *IdentifierTable::stringForId(Identifier i) const { - if (!i) + uint arrayIdx = i.asArrayIndex(); + if (arrayIdx < UINT_MAX) + return engine->newString(QString::number(arrayIdx)); + if (!i.isValid()) return nullptr; uint idx = i.id % alloc; @@ -197,6 +204,66 @@ Heap::String *IdentifierTable::stringForId(Identifier i) const } } +void IdentifierTable::markObjects(MarkStack *markStack) +{ + for (const auto &h : idHashes) + h->markObjects(markStack); +} + +template <typename Key> +int sweepTable(Heap::String **table, int alloc, std::function<Key(Heap::String *)> f) { + int freed = 0; + Key lastKey = 0; + int lastEntry = -1; + int start = 0; + // start at an empty entry so we compress properly + for (; start < alloc; ++start) { + if (!table[start]) + break; + } + + for (int i = 0; i < alloc; ++i) { + int idx = (i + start) % alloc; + Heap::String *entry = table[idx]; + if (!entry) { + lastEntry = -1; + continue; + } + if (entry->isMarked()) { + if (lastEntry >= 0 && lastKey == f(entry)) { + Q_ASSERT(table[lastEntry] == nullptr); + table[lastEntry] = entry; + table[idx] = nullptr; + lastEntry = (lastEntry + 1) % alloc; + Q_ASSERT(table[lastEntry] == nullptr); + } + continue; + } + if (lastEntry == -1) { + lastEntry = idx; + lastKey = f(entry); + } + table[idx] = nullptr; + ++freed; + } + for (int i = 0; i < alloc; ++i) { + Heap::String *entry = table[i]; + if (!entry) + continue; + Q_ASSERT(entry->isMarked()); + } + return freed; +} + +void IdentifierTable::sweep() +{ + int f = sweepTable<int>(entriesByHash, alloc, [](Heap::String *entry) {return entry->hashValue(); }); + int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::String *entry) {return entry->identifier.id; }); + Q_UNUSED(f); + Q_ASSERT(f == freed); + size -= freed; +} + Identifier IdentifierTable::identifier(const QString &s) { return insertString(s)->identifier; diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index dce270e337..84e1ef2f4f 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -53,6 +53,7 @@ #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4engine_p.h" +#include <qset.h> #include <limits.h> QT_BEGIN_NAMESPACE @@ -69,6 +70,8 @@ struct IdentifierTable Heap::String **entriesByHash; Heap::String **entriesById; + QSet<IdentifierHashData *> idHashes; + void addEntry(Heap::String *str); public: @@ -94,15 +97,14 @@ public: Q_QML_PRIVATE_EXPORT Heap::String *stringForId(Identifier i) const; - void mark(MarkStack *markStack) { - for (int i = 0; i < alloc; ++i) { - Heap::String *entry = entriesByHash[i]; - if (!entry || entry->isMarked()) - continue; - entry->setMarkBit(); - Q_ASSERT(entry->vtable()->markObjects); - entry->vtable()->markObjects(entry, markStack); - } + void markObjects(MarkStack *markStack); + void sweep(); + + void addIdentifierHash(IdentifierHashData *h) { + idHashes.insert(h); + } + void removeIdentifierHash(IdentifierHashData *h) { + idHashes.remove(h); } }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 40b57e76e1..da37fda590 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -122,7 +122,7 @@ void PropertyHash::detach(bool grow, int classSize) PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); for (int i = 0; i < d->alloc; ++i) { const Entry &e = d->entries[i]; - if (!e.identifier || e.index >= static_cast<unsigned>(classSize)) + if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) continue; uint idx = e.identifier.id % dd->alloc; while (dd->entries[idx].identifier.isValid()) { @@ -294,7 +294,7 @@ Heap::InternalClass *InternalClass::changeMember(Identifier identifier, Property for (uint i = 0; i < size; ++i) { Identifier identifier = nameMap.at(i); PropertyHash::Entry e = { identifier, newClass->size }; - if (!identifier) + if (!identifier.isValid()) e.identifier = nameMap.at(i - 1); newClass->propertyTable.addEntry(e, newClass->size); newClass->nameMap.add(newClass->size, identifier); @@ -630,6 +630,12 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) ic->prototype->mark(stack); if (ic->parent) ic->parent->mark(stack); + + for (uint i = 0; i < ic->size; ++i) { + Identifier id = ic->nameMap.at(i); + if (Heap::Base *b = id.asHeapObject()) + b->mark(stack); + } } } diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index a2c7620910..290251f4ba 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -134,7 +134,7 @@ inline uint PropertyHash::lookup(Identifier identifier) const while (1) { if (d->entries[idx].identifier == identifier) return d->entries[idx].index; - if (!d->entries[idx].identifier) + if (!d->entries[idx].identifier.isValid()) return UINT_MAX; ++idx; idx %= d->alloc; @@ -258,8 +258,6 @@ struct InternalClassTransition { return id < other.id || (id == other.id && flags < other.flags); } }; -static_assert(sizeof(Identifier) == sizeof(VTable *), "Identifier needs to be one pointer large to map into the union above"); - namespace Heap { struct InternalClass : Base { diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 59540611b1..a896caaecb 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -531,7 +531,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * while (it->memberIndex < o->internalClass()->size) { Identifier n = o->internalClass()->nameMap.at(it->memberIndex); - if (!n) { + if (!n.isValid()) { // accessor properties have a dummy entry with n == 0 ++it->memberIndex; continue; diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index d14dfd082f..81044103eb 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -52,9 +52,17 @@ using namespace QV4; #ifndef V4_BOOTSTRAP +void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack) +{ + StringOrSymbol *s = static_cast<StringOrSymbol *>(that); + Heap::Base *id = s->identifier.asHeapObject(); + if (id) + id->mark(markStack); +} + void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) { - Base::markObjects(that, markStack); + StringOrSymbol::markObjects(that, markStack); String *s = static_cast<String *>(that); if (s->subtype < StringType_Complex) return; @@ -250,4 +258,3 @@ uint String::toArrayIndex(const QString &str) { return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); } - diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index a1763522b5..4e35853f69 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -68,6 +68,15 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base { mutable QStringData *text; mutable Identifier identifier; + static void markObjects(Heap::Base *that, MarkStack *markStack); + + inline QString toQString() const { + if (!text) + return QString(); + QStringDataPtr ptr = { text }; + text->ref.ref(); + return QString(ptr); + } }; struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 06313c3d30..795fb84ec4 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -42,6 +42,7 @@ #include "qv4objectproto_p.h" #include "qv4mm_p.h" #include "qv4qobjectwrapper_p.h" +#include "qv4identifiertable_p.h" #include <QtCore/qalgorithms.h> #include <QtCore/private/qnumeric_p.h> #include <QtCore/qloggingcategory.h> @@ -1019,7 +1020,9 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt } } + if (!lastSweep) { + engine->identifierTable->sweep(); blockAllocator.sweep(/*classCountPtr*/); hugeItemAllocator.sweep(classCountPtr); icAllocator.sweep(/*classCountPtr*/); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 81f1f89c92..350d9b09a2 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -241,7 +241,7 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) QV4::ScopedObject o(scope); for (uint i = 0; i < frozen->size; ++i) { - if (!frozen->nameMap.at(i)) + if (!frozen->nameMap.at(i).isValid()) continue; o = *object->propertyData(i); if (o) |