From 516523750dbae4fdabed5b09281fdb2da843941e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Mar 2019 14:03:26 +0100 Subject: [PATCH 1/9] Enable lookups for imported scripts and worker scripts This is straight-forward to enable, with the minor adjustment that we need to handle the case where a global lookup is done without a calling qml context (worker script). We don't know at compile time whether a script will be imported directly or used as a worker script, so we have to generate the global qml lookup instruction regardless and handle it at run-time. Change-Id: Ia033afa214d919d906c676498dd3eceb1c5639d8 Reviewed-by: Michael Brasser Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compilercontext.cpp | 2 +- src/qml/jsruntime/qv4qmlcontext.cpp | 19 ++++++++++++++++--- src/qml/jsruntime/qv4script.cpp | 1 - tools/qmlcachegen/qmlcachegen.cpp | 1 - 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 84c5d67e8d..d1a5fee92b 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/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(ctx.d())->locals[index].asReturnedValue(); } - Scoped qmlContext(scope, engine->qmlContext()->qml()); bool hasProperty = false; - ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr, - &hasProperty, base, l)); + ScopedValue result(scope); + + Scoped callingQmlContext(scope, engine->qmlContext()); + if (callingQmlContext) { + Scoped 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 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/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index d6fae446a1..b6b80a4461 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -301,7 +301,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 jsErrors = v4CodeGen.errors(); From 156066d4753243143970aeb0b740f8f429e7916b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 25 Mar 2019 15:18:16 +0100 Subject: [PATCH 2/9] Restore rich Text creation speed 634b59f187c8697f03a606cbacb5f0a69650ea7c caused a significant performance regression during layout of any Text item with RichText, by calling QQuickTextDocumentWithImageResources::setPageSize() with an accurate width in ensureDoc(); but ensureDoc() is called multiple times, and doesn't need to calculate actual layout. The accurate width is needed (at least) when doing layout of HTML tables that contain percentage-based column widths. For that purpose it's enough to set the correct "page" size in updateSize(). Fixes: QTBUG-74638 Task-number: QTBUG-72457 Change-Id: I34ab085316018576eba64a67200c527bb64b9533 Reviewed-by: Simon Hausmann --- src/quick/items/qquicktext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 8b6f59497f..97cc33b95f 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1247,7 +1247,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()), From d8110b53ed9ee4d69b92e602e812c6311c1b863b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Mar 2019 17:15:29 +0100 Subject: [PATCH 3/9] Trigger the garbage collector when allocating InternalClass objects As we check the icAllocator's slots on shouldRunGC() we should also check shouldRunGC() when adding slots. Otherwise we might never run the GC when only allocating InternalClasses. In addition, account for the "unmanaged" size of the PropertyAttributes that are part of the InternalClass objects. Those can be large. In cases where an excessive number of large InternalClass objects is created the garbage collector is now invoked frequently, which costs a significant number of CPU cycles, but prevents the memory usage from growing indefinitely. Task-number: QTBUG-58559 Change-Id: Icf102cb6100f6dba212b8bffe1c178897880eda0 Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4internalclass.cpp | 56 +++++++++++++++++++++++++ src/qml/jsruntime/qv4internalclass_p.h | 57 ++++++++------------------ src/qml/memory/qv4mm.cpp | 55 ++----------------------- src/qml/memory/qv4mm_p.h | 44 +++++++++++++++++++- tests/auto/qml/qv4mm/tst_qv4mm.cpp | 52 ++++++++++++++--------- 5 files changed, 151 insertions(+), 113 deletions(-) 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::mark(MarkStack *s) data->mark(s); } +SharedInternalClassDataPrivate::SharedInternalClassDataPrivate( + const SharedInternalClassDataPrivate &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::SharedInternalClassDataPrivate( + const SharedInternalClassDataPrivate &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::~SharedInternalClassDataPrivate() +{ + m_engine->memoryManager->changeUnmanagedHeapSizeUsage( + -qptrdiff(m_alloc * sizeof(PropertyAttributes))); + delete [] data; +} + +void SharedInternalClassDataPrivate::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 -struct SharedInternalClassDataPrivate { - SharedInternalClassDataPrivate(ExecutionEngine *) +template +struct SharedInternalClassDataPrivate {}; + +template<> +struct SharedInternalClassDataPrivate { + 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 &other); + SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &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/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 #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. void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } template 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(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/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); + QV4::ExecutionEngine engine; + QV4::Scope scope(engine.rootContext()); + QV4::ScopedObject object(scope, engine.newObject()); - // It should garbage collect the parents of the internalClass, - // as those aren't used anywhere else. - QCOMPARE(v4Object->internalClass->parent, nullptr); + // 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 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) From 82247946b2733bcd5bb3740a31c7d38fb90f6ddd Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Fri, 22 Mar 2019 14:30:15 +0100 Subject: [PATCH 4/9] Fix text wrap: do not break on last line if right elide is enabled For multiline texts with word wrapping, it is not so meaningful to add breaks in the last line if it is anyway going to be elided. Fix by using the WrapAnywhere strategy for the last line for such situations. Fixes: QTBUG-72736 Change-Id: I1263c81277b6ca89ba461529fced1094263c026f Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktext.cpp | 5 + .../data/text/text_wrap_elide_maxlines.qml | 133 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 73b151168e..d7307daa02 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -754,6 +754,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) bool once = true; int elideStart = 0; int elideEnd = 0; + bool noBreakLastLine = multilineElide && (wrapMode == QQuickText::Wrap || wrapMode == QQuickText::WordWrap); int eos = multilengthEos; @@ -786,11 +787,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) QRectF unelidedRect; QTextLine line = layout.createLine(); for (visibleCount = 1; ; ++visibleCount) { + if (noBreakLastLine && visibleCount == maxLineCount) + layout.engine()->option.setWrapMode(QTextOption::WrapAnywhere); if (customLayout) { setupCustomLineGeometry(line, naturalHeight); } else { setLineGeometry(line, lineWidth, naturalHeight); } + if (noBreakLastLine && visibleCount == maxLineCount) + layout.engine()->option.setWrapMode(QTextOption::WrapMode(wrapMode)); unelidedRect = br.united(line.naturalTextRect()); diff --git a/tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml b/tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml new file mode 100644 index 0000000000..927f2b3148 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml @@ -0,0 +1,133 @@ +import QtQuick 2.0 + +//test wrapping and elision when maximumLineCount is set + +Item { + width: 320 + height: 480 + Rectangle { + id: text_area + color: "light yellow" + x: 50 + y: 0 + height: parent.height + width: 150 + } + Text { + id: text_0000 + wrapMode: Text.WrapAnywhere + text: "The quick brown fox jumps over the lazy dog." + x: text_area.x + y: text_area.y + width: text_area.width + maximumLineCount: 2 + elide: Text.ElideRight + color: "red" + font.family: "Arial" + font.pixelSize: 22 + } + Text { + id: text_0001 + wrapMode: Text.Wrap + text: text_0000.text + anchors.top: text_0000.bottom + anchors.left: text_0000.left + width: text_0000.width + maximumLineCount: text_0000.maximumLineCount + elide: Text.ElideRight + color: "blue" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0002 + wrapMode: Text.WordWrap + text: text_0000.text + anchors.top: text_0001.bottom + anchors.left: text_0000.left + width: text_0000.width + maximumLineCount: text_0000.maximumLineCount + elide: Text.ElideRight + color: "green" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0003 + wrapMode: Text.WrapAnywhere + text: "ABCDEFGHIJKL 1234567890123" + anchors.top: text_0002.bottom + anchors.left: text_0000.left + width: 150 + maximumLineCount: 2 + elide: Text.ElideRight + color: "red" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0004 + wrapMode: Text.Wrap + text: text_0003.text + anchors.top: text_0003.bottom + anchors.left: text_0000.left + width: text_0000.width + maximumLineCount: text_0000.maximumLineCount + elide: Text.ElideRight + color: "blue" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0005 + wrapMode: Text.WordWrap + text: text_0003.text + anchors.top: text_0004.bottom + anchors.left: text_0000.left + width: text_0000.width + maximumLineCount: text_0000.maximumLineCount + elide: Text.ElideRight + color: "green" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0006 + wrapMode: Text.WrapAnywhere + text: "The quick brown 1234567890123" + anchors.top: text_0005.bottom + anchors.left: text_0000.left + width: 150 + maximumLineCount: 2 + elide: Text.ElideRight + color: "red" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0007 + wrapMode: Text.Wrap + text: text_0006.text + anchors.top: text_0006.bottom + anchors.left: text_0000.left + width: text_0000.width + maximumLineCount: text_0000.maximumLineCount + elide: Text.ElideRight + color: "blue" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } + Text { + id: text_0008 + wrapMode: Text.WordWrap + text: text_0006.text + anchors.top: text_0007.bottom + anchors.left: text_0000.left + width: text_0000.width + maximumLineCount: text_0000.maximumLineCount + elide: Text.ElideRight + color: "green" + font.family: text_0000.font.family + font.pixelSize: text_0000.font.pixelSize + } +} From 1b43149decdf42e47405a9e43840b7382076cb0d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 21 Mar 2019 14:36:57 +0100 Subject: [PATCH 5/9] Fix incorrect PathView.currentIndex assignment with StrictlyEnforceRange If currentIndex is quickly assigned a different index and then the previous current index again, it should not move. Fixes: QTBUG-74508 Change-Id: I8d610e3fe452c8631e082c648e77d2cb70ae57c5 Reviewed-by: Richard Moe Gustavsen --- src/quick/items/qquickpathview.cpp | 4 -- .../quick/qquickpathview/data/pathview5.qml | 65 +++++++++++++++++++ .../qquickpathview/tst_qquickpathview.cpp | 23 +++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/auto/quick/qquickpathview/data/pathview5.qml diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 77ed8a659c..fd5814d2b2 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -2392,10 +2392,6 @@ void QQuickPathViewPrivate::snapToIndex(int index, MovementReason reason) return; qreal targetOffset = std::fmod(qreal(modelCount - index), qreal(modelCount)); - - if (offset == targetOffset) - return; - moveReason = reason; offsetAdj = 0.0; tl.reset(moveOffset); diff --git a/tests/auto/quick/qquickpathview/data/pathview5.qml b/tests/auto/quick/qquickpathview/data/pathview5.qml new file mode 100644 index 0000000000..479c5dc500 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/pathview5.qml @@ -0,0 +1,65 @@ +import QtQuick 2.0 + +PathView { + property int countclick: 0 + id: pathview + y: 0 + width: 348 + height: 480 + + interactive: false + + cacheItemCount: 10 + currentIndex: 2 + pathItemCount: 4 + highlightMoveDuration: 1000 + highlightRangeMode : PathView.StrictlyEnforceRange + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + snapMode : PathView.SnapOneItem + + path: Path { + id: leftPath + startX: pathview.width / 2 - 800 + startY: pathview.height / 2 - 800 + + PathArc { + x: pathview.width / 2 - 800 + y: pathview.height / 2 + 800 + radiusX: 800 + radiusY: 800 + direction: PathArc.Clockwise + } + } + + model: ListModel { + id: model + ListElement { objectName:"aqua"; name: "aqua" ;mycolor:"aqua"} + ListElement { objectName:"blue"; name: "blue" ;mycolor:"blue"} + ListElement { objectName:"blueviolet"; name: "blueviolet" ;mycolor:"blueviolet"} + ListElement { objectName:"brown"; name: "brown" ;mycolor:"brown"} + ListElement { objectName:"chartreuse"; name: "chartreuse" ;mycolor:"chartreuse"} + } + + delegate: Item { + id: revolveritem + objectName: model.objectName + + width: pathview.width + height: pathview.height + + Rectangle + { + id:myRectangle + color: mycolor + width: pathview.width -20 + height: pathview.height -20 + + Text { + anchors.centerIn: parent + text: "index:"+index + color: "white" + } + } + } +} diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 6267a3771e..7491df5087 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -103,6 +103,7 @@ private slots: void offset_data(); void offset(); void setCurrentIndex(); + void setCurrentIndexWrap(); void resetModel(); void propertyChanges(); void pathChanges(); @@ -1137,6 +1138,28 @@ void tst_QQuickPathView::setCurrentIndex() QCOMPARE(currentIndexSpy.count(), 1); } +void tst_QQuickPathView::setCurrentIndexWrap() +{ + QScopedPointer window(createView()); + window->setSource(testFileUrl("pathview5.qml")); + window->show(); + qApp->processEvents(); + + QQuickPathView *pathview = qobject_cast(window->rootObject()); + QVERIFY(pathview); + + // set current index to last item + pathview->setCurrentIndex(4); + // set currentIndex to first item, then quickly set it back (QTBUG-74508) + QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged())); + QSignalSpy movementStartedSpy(pathview, SIGNAL(movementStarted())); + pathview->setCurrentIndex(0); + pathview->setCurrentIndex(4); + QCOMPARE(pathview->currentIndex(), 4); + QCOMPARE(currentIndexSpy.count(), 2); + QCOMPARE(movementStartedSpy.count(), 0); +} + void tst_QQuickPathView::resetModel() { QScopedPointer window(createView()); From b6d0ed435d2d57359b6eafb2728c024eab0b1ece Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Mar 2019 15:43:13 +0100 Subject: [PATCH 6/9] Scope intermediate values when allocating objects Otherwise the garbage collector might interfere and we might end up with dangling pointers in random places. Task-number: QTBUG-74190 Change-Id: I253c526ef930b8f0be14e96a42af8b66fda3b22d Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4arrayobject.cpp | 4 +++- src/qml/jsruntime/qv4engine.cpp | 16 +++++++++++----- src/qml/jsruntime/qv4generatorobject.cpp | 4 +++- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 13 +++++++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 8637db3dfd..b5b421fa39 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -103,7 +103,9 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1); ctor->addSymbolSpecies(); - ScopedObject unscopables(scope, engine->newObject(engine->classes[EngineBase::Class_Empty]->changeVTable(QV4::Object::staticVTable()))); + Scoped ic(scope, engine->classes[EngineBase::Class_Empty] + ->changeVTable(QV4::Object::staticVTable())); + ScopedObject unscopables(scope, engine->newObject(ic->d())); ScopedString name(scope); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 1d0c7c13fa..bcd577c24d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -464,11 +464,17 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsObjects[TypeError_Ctor] = memoryManager->allocate(global); jsObjects[URIError_Ctor] = memoryManager->allocate(global); jsObjects[IteratorProto] = memoryManager->allocate(); - jsObjects[ForInIteratorProto] = memoryManager->allocObject(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[MapIteratorProto] = memoryManager->allocObject(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[SetIteratorProto] = memoryManager->allocObject(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[ArrayIteratorProto] = memoryManager->allocObject(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[StringIteratorProto] = memoryManager->allocObject(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype())); + + ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[ForInIteratorProto] = memoryManager->allocObject(ic); + ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[MapIteratorProto] = memoryManager->allocObject(ic); + ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[SetIteratorProto] = memoryManager->allocObject(ic); + ic = newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[ArrayIteratorProto] = memoryManager->allocObject(ic); + ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[StringIteratorProto] = memoryManager->allocObject(ic); str = newString(QStringLiteral("get [Symbol.species]")); jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0); diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 566db6fd4e..14caa6953f 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -139,7 +139,9 @@ void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedValue v(scope); - ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype()))); + Scoped ic(scope, engine->newInternalClass( + Object::staticVTable(), engine->functionPrototype())); + ScopedObject ctorProto(scope, engine->newObject(ic->d())); ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 0491ccd473..0b1f1e0bca 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -236,6 +236,7 @@ private slots: void importExportErrors(); void equality(); + void aggressiveGc(); public: Q_INVOKABLE QJSValue throwingCppMethod1(); @@ -4632,6 +4633,18 @@ void tst_QJSEngine::equality() QCOMPARE(ok.toString(), QString("ok")); } +void tst_QJSEngine::aggressiveGc() +{ + const QByteArray origAggressiveGc = qgetenv("QV4_MM_AGGRESSIVE_GC"); + qputenv("QV4_MM_AGGRESSIVE_GC", "true"); + { + QJSEngine engine; // ctor crashes if core allocation methods don't properly scope things. + QJSValue obj = engine.newObject(); + QVERIFY(obj.isObject()); + } + qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" From 60f766f5c68fc33322c6d095d81b1856828b2b0b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 26 Mar 2019 13:57:33 +0100 Subject: [PATCH 7/9] Re-use existing IC when freezing properties There is no reason to start from the empty class in that case. Furthermore, if the properties are already frozen, starting from the empty class will walk the IC hierarchy to the current IC. However, if the garbage collector has removed the intermediate classes in the mean time, we end up at a new IC which is equivalent but not the same. Therefore, the freezing never terminates. Task-number: QTBUG-74190 Change-Id: Id544bd00d3b4b563fb06dfce0edd0385e1d32a6c Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4internalclass.cpp | 7 +++---- src/qml/jsruntime/qv4internalclass_p.h | 2 +- tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 13 +++++++++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 8e2657f4a5..3fb2465a45 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -658,11 +658,10 @@ Heap::InternalClass *InternalClass::frozen() return f; } -Heap::InternalClass *InternalClass::propertiesFrozen() const +Heap::InternalClass *InternalClass::propertiesFrozen() { Scope scope(engine); - Scoped frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable)); - frozen = frozen->changePrototype(prototype); + Scoped frozen(scope, this); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (!nameMap.at(i).isValid()) @@ -671,7 +670,7 @@ Heap::InternalClass *InternalClass::propertiesFrozen() const attrs.setWritable(false); attrs.setConfigurable(false); } - frozen = frozen->addMember(nameMap.at(i), attrs); + frozen = frozen->changeMember(nameMap.at(i), attrs); } return frozen->d(); } diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index c689637721..121238c555 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -428,7 +428,7 @@ struct InternalClass : Base { Q_REQUIRED_RESULT InternalClass *sealed(); Q_REQUIRED_RESULT InternalClass *frozen(); - Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const; + Q_REQUIRED_RESULT InternalClass *propertiesFrozen(); Q_REQUIRED_RESULT InternalClass *asProtoClass(); diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index f58ae38264..b9cb6b70d2 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -80,6 +80,7 @@ private slots: void qrcUrls(); void cppSignalAndEval(); void singletonInstance(); + void aggressiveGc(); public slots: QObject *createAQObjectForOwnershipTest () @@ -1043,6 +1044,18 @@ void tst_qqmlengine::singletonInstance() } } +void tst_qqmlengine::aggressiveGc() +{ + const QByteArray origAggressiveGc = qgetenv("QV4_MM_AGGRESSIVE_GC"); + qputenv("QV4_MM_AGGRESSIVE_GC", "true"); + { + QQmlEngine engine; // freezing should not run into infinite recursion + QJSValue obj = engine.newObject(); + QVERIFY(obj.isObject()); + } + qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" From 1e18f2c4a647923fc66a3e3204fcccd88a2960a6 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 26 Mar 2019 09:40:03 +0100 Subject: [PATCH 8/9] Check for numeric limits when growing SharedInternalClassDataPrivate We can effectively only deal with values of < 2GB for m_alloc * sizeof(Data). This is not much more than the values seen in the wild. Change-Id: Ia6972df33d34a320b5b087d38db81aae24ce5bbe Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4internalclass.cpp | 28 +++++++++++++++++--------- src/qml/jsruntime/qv4internalclass_p.h | 8 ++++++-- src/qml/jsruntime/qv4memberdata.cpp | 21 +++++++++++++++---- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 3fb2465a45..a10fda79f2 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -145,7 +145,7 @@ SharedInternalClassDataPrivate::SharedInternalClassDataPrivate(cons data(nullptr) { if (other.alloc()) { - int s = other.size(); + const uint s = other.size(); data = MemberData::allocate(engine, other.alloc(), other.data); setSize(s); } @@ -164,8 +164,8 @@ SharedInternalClassDataPrivate::SharedInternalClassDataPrivate(cons void SharedInternalClassDataPrivate::grow() { - uint a = alloc() * 2; - int s = size(); + const uint a = alloc() * 2; + const uint s = size(); data = MemberData::allocate(engine, a, data); setSize(s); Q_ASSERT(alloc() >= a); @@ -209,10 +209,11 @@ SharedInternalClassDataPrivate::SharedInternalClassDataPriva const SharedInternalClassDataPrivate &other, uint pos, PropertyAttributes value) : refcount(1), - m_alloc(pos + 8), + m_alloc(qMin(other.m_alloc, pos + 8)), m_size(pos + 1), m_engine(other.m_engine) { + Q_ASSERT(m_size <= m_alloc); m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes)); data = new PropertyAttributes[m_alloc]; if (other.data) @@ -244,22 +245,29 @@ SharedInternalClassDataPrivate::~SharedInternalClassDataPriv } void SharedInternalClassDataPrivate::grow() { + uint alloc; if (!m_alloc) { - m_alloc = 4; - m_engine->memoryManager->changeUnmanagedHeapSizeUsage( - 2 * m_alloc * sizeof(PropertyAttributes)); + alloc = 8; + m_engine->memoryManager->changeUnmanagedHeapSizeUsage(alloc * sizeof(PropertyAttributes)); } else { + // yes, signed. We don't want to deal with stuff > 2G + const uint currentSize = m_alloc * sizeof(PropertyAttributes); + if (currentSize < uint(std::numeric_limits::max() / 2)) + alloc = m_alloc * 2; + else + alloc = std::numeric_limits::max() / sizeof(PropertyAttributes); + m_engine->memoryManager->changeUnmanagedHeapSizeUsage( - m_alloc * sizeof(PropertyAttributes)); + (alloc - m_alloc) * sizeof(PropertyAttributes)); } - auto *n = new PropertyAttributes[m_alloc * 2]; + auto *n = new PropertyAttributes[alloc]; if (data) { memcpy(n, data, m_alloc*sizeof(PropertyAttributes)); delete [] data; } data = n; - m_alloc *= 2; + m_alloc = alloc; } namespace Heap { diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 121238c555..42b61218a5 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -247,8 +247,12 @@ struct SharedInternalClassData { Q_ASSERT(pos == d->size()); if (pos == d->alloc()) d->grow(); - d->setSize(d->size() + 1); - d->set(pos, value); + if (pos >= d->alloc()) { + qBadAlloc(); + } else { + d->setSize(d->size() + 1); + d->set(pos, value); + } } void set(uint pos, T value) { diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index 246f857643..f327c85001 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -69,12 +69,25 @@ Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberD size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value)); // round up to next power of two to avoid quadratic behaviour for very large objects alloc = nextPowerOfTwo(alloc); - Heap::MemberData *m = e->memoryManager->allocManaged(alloc); - if (old) + + // The above code can overflow in a number of interesting ways. All of those are unsigned, + // and therefore defined behavior. Still, apply some sane bounds. + if (alloc > std::numeric_limits::max()) + alloc = std::numeric_limits::max(); + + Heap::MemberData *m; + if (old) { + const size_t oldSize = sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value); + if (oldSize > alloc) + alloc = oldSize; + m = e->memoryManager->allocManaged(alloc); // no write barrier required here - memcpy(m, old, sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value)); - else + memcpy(m, old, oldSize); + } else { + m = e->memoryManager->allocManaged(alloc); m->init(); + } + m->values.alloc = static_cast((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); m->values.size = m->values.alloc; return m; From 3507c29e8d9ea42e63fd0c01e27ee9263d939256 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 27 Mar 2019 10:20:38 +0100 Subject: [PATCH 9/9] Blacklist tst_qquickfolderlistmodel::nameFilters() on msvc2015/17 Task-number: QTBUG-74739 Change-Id: Ib0f1d39cb5bc9864195b3c62d254fa38370faf0d Reviewed-by: Shawn Rutledge --- tests/auto/qml/qquickfolderlistmodel/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/auto/qml/qquickfolderlistmodel/BLACKLIST diff --git a/tests/auto/qml/qquickfolderlistmodel/BLACKLIST b/tests/auto/qml/qquickfolderlistmodel/BLACKLIST new file mode 100644 index 0000000000..642fdea741 --- /dev/null +++ b/tests/auto/qml/qquickfolderlistmodel/BLACKLIST @@ -0,0 +1,3 @@ +[nameFilters] +msvc-2015 +msvc-2017