diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-29 01:00:40 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-29 01:00:40 +0100 |
commit | f385f6b39f2b60f1f6c59973a030b03437abbef2 (patch) | |
tree | c0ec45ee3f20b82b7f42e784fb10616edb8a2c75 | |
parent | d9f849b33591458fbb66724e4e6d59a1b87678f2 (diff) | |
parent | 4407f1f81813216bf64021eab7dcecc88d3056fe (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I9ba374f0c652628b7c84c36893c32b22529e384f
21 files changed, 480 insertions, 146 deletions
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 7586611035..52215c2ce6 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/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index b975d02d9d..b3e607d74a 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<InternalClass> 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 8a849ddf5f..fdc2118840 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -470,11 +470,17 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global); jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global); jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>(); - jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype())); - jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype())); + + ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(ic); + ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(ic); + ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(ic); + ic = newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(ic); + ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()); + jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(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<InternalClass> 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/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9906e2b1a0..a10fda79f2 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 @@ -144,7 +145,7 @@ SharedInternalClassDataPrivate<PropertyKey>::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); } @@ -163,8 +164,8 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons void SharedInternalClassDataPrivate<PropertyKey>::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); @@ -204,7 +205,70 @@ void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s) data->mark(s); } +SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate( + const SharedInternalClassDataPrivate<PropertyAttributes> &other, uint pos, + PropertyAttributes value) + : refcount(1), + 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) + 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() { + uint alloc; + if (!m_alloc) { + 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<int>::max() / 2)) + alloc = m_alloc * 2; + else + alloc = std::numeric_limits<int>::max() / sizeof(PropertyAttributes); + + m_engine->memoryManager->changeUnmanagedHeapSizeUsage( + (alloc - m_alloc) * sizeof(PropertyAttributes)); + } + + auto *n = new PropertyAttributes[alloc]; + if (data) { + memcpy(n, data, m_alloc*sizeof(PropertyAttributes)); + delete [] data; + } + data = n; + m_alloc = alloc; +} namespace Heap { @@ -602,11 +666,10 @@ Heap::InternalClass *InternalClass::frozen() return f; } -Heap::InternalClass *InternalClass::propertiesFrozen() const +Heap::InternalClass *InternalClass::propertiesFrozen() { Scope scope(engine); - Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable)); - frozen = frozen->changePrototype(prototype); + Scoped<QV4::InternalClass> frozen(scope, this); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (!nameMap.at(i).isValid()) @@ -615,7 +678,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 681cbda5f9..42b61218a5 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<> @@ -270,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) { @@ -451,7 +432,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/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<MemberData>(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<int>::max()) + alloc = std::numeric_limits<int>::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<MemberData>(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<MemberData>(alloc); m->init(); + } + m->values.alloc = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); m->values.size = m->values.alloc; return m; 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 b3d9155ebc..34d334a24d 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/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index be8532bf64..e4480b335a 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -2419,10 +2419,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/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 73b151168e..6d343a91ce 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()); @@ -1247,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/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 4af5e1d850..4cc37f0380 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -243,6 +243,7 @@ private slots: void importExportErrors(); void equality(); + void aggressiveGc(); public: Q_INVOKABLE QJSValue throwingCppMethod1(); @@ -4798,6 +4799,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" 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" 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 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/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 bf38d2d926..1a5ce39318 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -104,6 +104,7 @@ private slots: void offset_data(); void offset(); void setCurrentIndex(); + void setCurrentIndexWrap(); void resetModel(); void propertyChanges(); void pathChanges(); @@ -1138,6 +1139,28 @@ void tst_QQuickPathView::setCurrentIndex() QCOMPARE(currentIndexSpy.count(), 1); } +void tst_QQuickPathView::setCurrentIndexWrap() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("pathview5.qml")); + window->show(); + qApp->processEvents(); + + QQuickPathView *pathview = qobject_cast<QQuickPathView*>(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<QQuickView> window(createView()); 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 + } +} 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(); |