diff options
-rw-r--r-- | src/qml/compiler/qv4compilercontext.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4internalclass.cpp | 56 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4internalclass_p.h | 57 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext.cpp | 19 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4script.cpp | 1 | ||||
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 55 | ||||
-rw-r--r-- | src/qml/memory/qv4mm_p.h | 44 | ||||
-rw-r--r-- | src/quick/items/qquicktext.cpp | 2 | ||||
-rw-r--r-- | tests/auto/qml/qv4mm/tst_qv4mm.cpp | 54 | ||||
-rw-r--r-- | tools/qmlcachegen/qmlcachegen.cpp | 1 |
10 files changed, 170 insertions, 121 deletions
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index afac8d0b4d..b9ab4e5173 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -190,7 +190,7 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS if (c->contextType == ContextType::Eval) return result; - if (c->contextType == ContextType::Binding) + if (c->contextType == ContextType::Binding || c->contextType == ContextType::ScriptImportedByQML) result.type = ResolvedName::QmlGlobal; else result.type = ResolvedName::Global; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9906e2b1a0..8e2657f4a5 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -44,6 +44,7 @@ #include "qv4object_p.h" #include "qv4identifiertable_p.h" #include "qv4value_p.h" +#include "qv4mm_p.h" QT_BEGIN_NAMESPACE @@ -204,7 +205,62 @@ void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s) data->mark(s); } +SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate( + const SharedInternalClassDataPrivate<PropertyAttributes> &other, uint pos, + PropertyAttributes value) + : refcount(1), + m_alloc(pos + 8), + m_size(pos + 1), + m_engine(other.m_engine) +{ + m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes)); + data = new PropertyAttributes[m_alloc]; + if (other.data) + memcpy(data, other.data, (m_size - 1) * sizeof(PropertyAttributes)); + data[pos] = value; +} + +SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate( + const SharedInternalClassDataPrivate<PropertyAttributes> &other) + : refcount(1), + m_alloc(other.m_alloc), + m_size(other.m_size), + m_engine(other.m_engine) +{ + if (m_alloc) { + m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes)); + data = new PropertyAttributes[m_alloc]; + memcpy(data, other.data, m_size*sizeof(PropertyAttributes)); + } else { + data = nullptr; + } +} +SharedInternalClassDataPrivate<PropertyAttributes>::~SharedInternalClassDataPrivate() +{ + m_engine->memoryManager->changeUnmanagedHeapSizeUsage( + -qptrdiff(m_alloc * sizeof(PropertyAttributes))); + delete [] data; +} + +void SharedInternalClassDataPrivate<PropertyAttributes>::grow() { + if (!m_alloc) { + m_alloc = 4; + m_engine->memoryManager->changeUnmanagedHeapSizeUsage( + 2 * m_alloc * sizeof(PropertyAttributes)); + } else { + m_engine->memoryManager->changeUnmanagedHeapSizeUsage( + m_alloc * sizeof(PropertyAttributes)); + } + + auto *n = new PropertyAttributes[m_alloc * 2]; + if (data) { + memcpy(n, data, m_alloc*sizeof(PropertyAttributes)); + delete [] data; + } + data = n; + m_alloc *= 2; +} namespace Heap { diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 681cbda5f9..c689637721 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -149,55 +149,31 @@ inline PropertyHash::Entry *PropertyHash::lookup(PropertyKey identifier) const } } -template<typename T> -struct SharedInternalClassDataPrivate { - SharedInternalClassDataPrivate(ExecutionEngine *) +template<class T> +struct SharedInternalClassDataPrivate {}; + +template<> +struct SharedInternalClassDataPrivate<PropertyAttributes> { + SharedInternalClassDataPrivate(ExecutionEngine *engine) : refcount(1), m_alloc(0), m_size(0), - data(nullptr) + data(nullptr), + m_engine(engine) { } - SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other) - : refcount(1), - m_alloc(other.m_alloc), - m_size(other.m_size) - { - if (m_alloc) { - data = new T[m_alloc]; - memcpy(data, other.data, m_size*sizeof(T)); - } - } - SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, T value) - : refcount(1), - m_alloc(pos + 8), - m_size(pos + 1) - { - data = new T[m_alloc]; - if (other.data) - memcpy(data, other.data, (m_size - 1)*sizeof(T)); - data[pos] = value; - } - ~SharedInternalClassDataPrivate() { delete [] data; } - + SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other); + SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other, + uint pos, PropertyAttributes value); + ~SharedInternalClassDataPrivate(); - void grow() { - if (!m_alloc) - m_alloc = 4; - T *n = new T[m_alloc * 2]; - if (data) { - memcpy(n, data, m_alloc*sizeof(T)); - delete [] data; - } - data = n; - m_alloc *= 2; - } + void grow(); uint alloc() const { return m_alloc; } uint size() const { return m_size; } void setSize(uint s) { m_size = s; } - T at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; } - void set(uint i, T t) { Q_ASSERT(data && i < m_alloc); data[i] = t; } + PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; } + void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; } void mark(MarkStack *) {} @@ -205,7 +181,8 @@ struct SharedInternalClassDataPrivate { private: uint m_alloc; uint m_size; - T *data; + PropertyAttributes *data; + ExecutionEngine *m_engine; }; template<> diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index a2c8e3916f..12ada7ee70 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -465,10 +465,23 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue(); } - Scoped<QQmlContextWrapper> qmlContext(scope, engine->qmlContext()->qml()); bool hasProperty = false; - ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr, - &hasProperty, base, l)); + ScopedValue result(scope); + + Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext()); + if (callingQmlContext) { + Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml()); + result = QQmlContextWrapper::getPropertyAndBase(qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty, + base, l); + } else { + // Code path typical to worker scripts, compiled with lookups but no qml context. + result = l->resolveGlobalGetter(engine); + if (l->globalGetter != Lookup::globalGetterGeneric) { + hasProperty = true; + l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter; + l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject; + } + } if (!hasProperty) return engine->throwReferenceError(name.toQString()); return result->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 7bbef3335e..6cb2e95cdc 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -201,7 +201,6 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi } Codegen cg(unitGenerator, /*strict mode*/false); - cg.setUseFastLookups(false); cg.generateFromProgram(fileName, finalUrl, source, program, module, contextType); errors = cg.qmlErrors(); if (!errors.isEmpty()) { diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 203f1f424f..cac68bdcaf 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -93,8 +93,6 @@ #include <pthread_np.h> #endif -#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024) - Q_LOGGING_CATEGORY(lcGcStats, "qt.qml.gc.statistics") Q_DECLARE_LOGGING_CATEGORY(lcGcStats) Q_LOGGING_CATEGORY(lcGcAllocatorStats, "qt.qml.gc.allocatorStats") @@ -759,7 +757,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) , hugeItemAllocator(chunkAllocator, engine) , m_persistentValues(new PersistentValueStorage(engine)) , m_weakValues(new PersistentValueStorage(engine)) - , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) + , unmanagedHeapSizeGCLimit(MinUnmanagedHeapSizeGCLimit) , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) , gcStats(lcGcStats().isDebugEnabled()) , gcCollectorStats(lcGcAllocatorStats().isDebugEnabled()) @@ -779,35 +777,9 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift; ++allocationCount; #endif - - bool didGCRun = false; - if (aggressiveGC) { - runGC(); - didGCRun = true; - } - unmanagedHeapSize += unmanagedSize; - if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) { - if (!didGCRun) - runGC(); - - if (3*unmanagedHeapSizeGCLimit <= 4*unmanagedHeapSize) - // more than 75% full, raise limit - unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit, unmanagedHeapSize) * 2; - else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) - // less than 25% full, lower limit - unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, unmanagedHeapSizeGCLimit/2); - didGCRun = true; - } - - HeapItem *m = blockAllocator.allocate(stringSize); - if (!m) { - if (!didGCRun && shouldRunGC()) - runGC(); - m = blockAllocator.allocate(stringSize, true); - } -// qDebug() << "allocated string" << m; + HeapItem *m = allocate(&blockAllocator, stringSize); memset(m, 0, stringSize); return *m; } @@ -819,32 +791,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size) ++allocationCount; #endif - bool didRunGC = false; - if (aggressiveGC) { - runGC(); - didRunGC = true; - } - Q_ASSERT(size >= Chunk::SlotSize); Q_ASSERT(size % Chunk::SlotSize == 0); -// qDebug() << "unmanagedHeapSize:" << unmanagedHeapSize << "limit:" << unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize; - - if (size > Chunk::DataSize) { - HeapItem *h = hugeItemAllocator.allocate(size); -// qDebug() << "allocating huge item" << h; - return *h; - } - - HeapItem *m = blockAllocator.allocate(size); - if (!m) { - if (!didRunGC && shouldRunGC()) - runGC(); - m = blockAllocator.allocate(size, true); - } - + HeapItem *m = allocate(&blockAllocator, size); memset(m, 0, size); -// qDebug() << "allocating data" << m; return *m; } diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index bbbbb1aef6..6dfdd81cb2 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -264,13 +264,13 @@ public: size_t getLargeItemsMem() const; // called when a JS object grows itself. Specifically: Heap::String::append + // and InternalClassDataPrivate<PropertyAttributes>. void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } template<typename ManagedType> typename ManagedType::Data *allocIC() { - size_t size = align(sizeof(typename ManagedType::Data)); - Heap::Base *b = *icAllocator.allocate(size, true); + Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data))); return static_cast<typename ManagedType::Data *>(b); } @@ -284,12 +284,52 @@ protected: Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers); private: + enum { + MinUnmanagedHeapSizeGCLimit = 128 * 1024 + }; + void collectFromJSStack(MarkStack *markStack) const; void mark(); void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr); bool shouldRunGC() const; void collectRoots(MarkStack *markStack); + HeapItem *allocate(BlockAllocator *allocator, std::size_t size) + { + bool didGCRun = false; + if (aggressiveGC) { + runGC(); + didGCRun = true; + } + + if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) { + if (!didGCRun) + runGC(); + + if (3*unmanagedHeapSizeGCLimit <= 4 * unmanagedHeapSize) { + // more than 75% full, raise limit + unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit, + unmanagedHeapSize) * 2; + } else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) { + // less than 25% full, lower limit + unmanagedHeapSizeGCLimit = qMax(std::size_t(MinUnmanagedHeapSizeGCLimit), + unmanagedHeapSizeGCLimit/2); + } + didGCRun = true; + } + + if (size > Chunk::DataSize) + return hugeItemAllocator.allocate(size); + + if (HeapItem *m = allocator->allocate(size)) + return m; + + if (!didGCRun && shouldRunGC()) + runGC(); + + return allocator->allocate(size, true); + } + public: QV4::ExecutionEngine *engine; ChunkAllocator *chunkAllocator; diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index d7307daa02..6d343a91ce 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1252,7 +1252,7 @@ void QQuickTextPrivate::ensureDoc() if (!extra.isAllocated() || !extra->doc) { Q_Q(QQuickText); extra.value().doc = new QQuickTextDocumentWithImageResources(q); - extra->doc->setPageSize(QSizeF(q->width(), -1)); + extra->doc->setPageSize(QSizeF(0, 0)); extra->doc->setDocumentMargin(0); extra->doc->setBaseUrl(q->baseUrl()); qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()), diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index b57b716ed6..1e34b79954 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -112,26 +112,40 @@ void tst_qv4mm::accessParentOnDestruction() void tst_qv4mm::clearICParent() { - QJSEngine engine; - QJSValue value = engine.evaluate( - "(function() {\n" - " var test = Object.create(null);\n" - " for (var i = 0; i < 100; i++)\n" - " test[(\"key_\"+i)] = true;\n" - " for (var i = 0; i < 100; i++)\n" - " delete test[\"key_\" + i];\n" - " return test;\n" - "})();" - ); - engine.collectGarbage(); - QV4::Value *v4Value = QJSValuePrivate::getValue(&value); - QVERIFY(v4Value); - QV4::Heap::Object *v4Object = v4Value->toObject(engine.handle()); - QVERIFY(v4Object); - - // It should garbage collect the parents of the internalClass, - // as those aren't used anywhere else. - QCOMPARE(v4Object->internalClass->parent, nullptr); + QV4::ExecutionEngine engine; + QV4::Scope scope(engine.rootContext()); + QV4::ScopedObject object(scope, engine.newObject()); + + // Keep identifiers in a separate array so that we don't have to allocate them in the loop that + // should test the GC on InternalClass allocations. + QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject()); + for (uint i = 0; i < 16 * 1024; ++i) { + QV4::Scope scope(&engine); + QV4::ScopedString s(scope); + s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i)); + identifiers->push_back(s); + + QV4::ScopedValue v(scope); + v->setDouble(i); + object->insertMember(s, v); + } + + // When allocating the InternalClass objects required for deleting properties, the GC should + // eventually run and remove all but the last two. + // If we ever manage to avoid allocating the InternalClasses in the first place we will need + // to change this test. + for (uint i = 0; i < 16 * 1024; ++i) { + QV4::Scope scope(&engine); + QV4::ScopedString s(scope, identifiers->getIndexed(i)); + QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass()); + QVERIFY(ic->d()->parent != nullptr); + object->deleteProperty(s->toPropertyKey()); + QVERIFY(object->internalClass() != ic->d()); + QCOMPARE(object->internalClass()->parent, ic->d()); + if (ic->d()->parent == nullptr) + return; + } + QFAIL("Garbage collector was not triggered by large amount of InternalClasses"); } QTEST_MAIN(tst_qv4mm) diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 1c9a004cb3..df7468eaef 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -305,7 +305,6 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, &irDocument.jsGenerator.stringTable, illegalNames); - v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program, &irDocument.jsModule, QV4::Compiler::ContextType::ScriptImportedByQML); QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors(); |