diff options
29 files changed, 482 insertions, 164 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/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index f6fb93eab3..1ab6e8c767 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1368,7 +1368,7 @@ QObject *QJSValue::toQObject() const \since 5.8 * If this QJSValue is a QMetaObject, returns the QMetaObject pointer - * that the QJSValue represents; otherwise, returns 0. + * that the QJSValue represents; otherwise, returns \nullptr. * * \sa isQMetaObject() */ 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<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 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<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 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/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index a31dbd1e08..a79d4d074c 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1331,7 +1331,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ QString url; const QStringRef absolutePath = absoluteFilePath.leftRef(absoluteFilePath.lastIndexOf(Slash) + 1); if (absolutePath.at(0) == Colon) - url = QLatin1String("qrc://") + absolutePath.mid(1); + url = QLatin1String("qrc") + absolutePath; else url = QUrl::fromLocalFile(absolutePath.toString()).toString(); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 8db0bc92b8..f801e9aeba 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -261,7 +261,8 @@ public: int index; mutable volatile bool isSetup:1; - mutable volatile bool isEnumSetup:1; + mutable volatile bool isEnumFromCacheSetup:1; + mutable volatile bool isEnumFromBaseSetup:1; mutable bool haveSuperType:1; mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects; mutable QStringHash<int> enums; @@ -367,7 +368,8 @@ QHash<const QMetaObject *, int> QQmlTypePrivate::attachedPropertyIds; QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) : refCount(1), regType(type), iid(nullptr), typeId(0), listId(0), revision(0), containsRevisionedAttributes(false), baseMetaObject(nullptr), - index(-1), isSetup(false), isEnumSetup(false), haveSuperType(false) + index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false), + haveSuperType(false) { switch (type) { case QQmlType::CppType: @@ -829,19 +831,24 @@ void QQmlTypePrivate::init() const void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const { - if (isEnumSetup) return; + if ((isEnumFromBaseSetup || !baseMetaObject) + && (isEnumFromCacheSetup || !cache)) { + return; + } init(); QMutexLocker lock(metaTypeDataLock()); - if (isEnumSetup) return; - if (cache) + if (!isEnumFromCacheSetup && cache) { insertEnumsFromPropertyCache(cache); - if (baseMetaObject) // could be singleton type without metaobject - insertEnums(baseMetaObject); + isEnumFromCacheSetup = true; + } - isEnumSetup = true; + if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject + insertEnums(baseMetaObject); + isEnumFromBaseSetup = true; + } } void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const 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/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()), 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" 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/qqmlmetatype/data/Components/App.qml b/tests/auto/qml/qqmlmetatype/data/Components/App.qml new file mode 100644 index 0000000000..3792ca665e --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/Components/App.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQml 2.0 + +import Components 1.0 + +QtObject { + id: mainRect + + property int appState: App.AppState.Blue + property string color: "blue" + + enum AppState { + Red, + Green, + Blue + } + + onAppStateChanged: { + if (appState === App.AppState.Green) + mainRect.color = "green" + else if (appState === App.AppState.Red) + mainRect.color = "red" + } + + property Timer timer: Timer { + onTriggered: appState = App.AppState.Green + running: true + interval: 100 + } +} diff --git a/tests/auto/qml/qqmlmetatype/data/Components/qmldir b/tests/auto/qml/qqmlmetatype/data/Components/qmldir new file mode 100644 index 0000000000..3f6db4ed2d --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/Components/qmldir @@ -0,0 +1,3 @@ +module Components + +App 1.0 App.qml diff --git a/tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml b/tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml new file mode 100644 index 0000000000..eef6abc6e5 --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 + +import Components 1.0 + +QtObject { + property App app: App { + appState: 0 + } + + property string color: app.color +} diff --git a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro index 345bc59615..109de7d212 100644 --- a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro +++ b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro @@ -10,4 +10,11 @@ qmlfiles.files = data/CompositeType.qml qmlfiles.prefix = /tstqqmlmetatype RESOURCES += qmlfiles +qmldirresource.files = \ + data/Components/App.qml \ + data/Components/qmldir \ + data/enumsInRecursiveImport.qml +qmldirresource.prefix = / +RESOURCES += qmldirresource + QT += core-private gui-private qml-private testlib diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index ce72f40dcc..7f103dc5ed 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -67,6 +67,9 @@ private slots: void normalizeUrls(); void unregisterAttachedProperties(); void revisionedGroupedProperties(); + + void enumsInRecursiveImport_data(); + void enumsInRecursiveImport(); }; class TestType : public QObject @@ -629,6 +632,35 @@ void tst_qqmlmetatype::revisionedGroupedProperties() } } +void tst_qqmlmetatype::enumsInRecursiveImport_data() +{ + QTest::addColumn<QString>("importPath"); + QTest::addColumn<QUrl>("componentUrl"); + + QTest::addRow("data directory") << dataDirectory() + << testFileUrl("enumsInRecursiveImport.qml"); + + // The qrc case behaves differently because we failed to detect the recursion in type loading + // due to varying numbers of slashes after the "qrc:" in the URLs. + QTest::addRow("resources") << QStringLiteral("qrc:/data") + << QUrl("qrc:/data/enumsInRecursiveImport.qml"); +} + +void tst_qqmlmetatype::enumsInRecursiveImport() +{ + QFETCH(QString, importPath); + QFETCH(QUrl, componentUrl); + + qmlClearTypeRegistrations(); + QQmlEngine engine; + engine.addImportPath(importPath); + QQmlComponent c(&engine, componentUrl); + QVERIFY(c.isReady()); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(!obj.isNull()); + QTRY_COMPARE(obj->property("color").toString(), QString("green")); +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.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 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<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/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<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors(); diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index c1399c38e5..8b1a679183 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -612,6 +612,19 @@ int main(int argc, char ** argv) fprintf(stderr, "%s\n", qPrintable(component->errorString())); return -1; } + + // Set default surface format before creating the window + QSurfaceFormat surfaceFormat; + if (options.multisample) + surfaceFormat.setSamples(16); + if (options.transparent) + surfaceFormat.setAlphaBufferSize(8); + if (options.coreProfile) { + surfaceFormat.setVersion(4, 1); + surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); + } + QSurfaceFormat::setDefaultFormat(surfaceFormat); + QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(topLevel)); if (window) { engine.setIncubationController(window->incubationController()); @@ -635,19 +648,11 @@ int main(int argc, char ** argv) if (options.verbose) new DiagnosticGlContextCreationListener(window.data()); #endif - QSurfaceFormat surfaceFormat = window->requestedFormat(); - if (options.multisample) - surfaceFormat.setSamples(16); if (options.transparent) { - surfaceFormat.setAlphaBufferSize(8); window->setClearBeforeRendering(true); window->setColor(QColor(Qt::transparent)); window->setFlags(Qt::FramelessWindowHint); } - if (options.coreProfile) { - surfaceFormat.setVersion(4, 1); - surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); - } window->setFormat(surfaceFormat); if (window->flags() == Qt::Window) // Fix window flags unless set by QML. |