Merge remote-tracking branch 'origin/5.13' into dev

Change-Id: I9ba374f0c652628b7c84c36893c32b22529e384f
This commit is contained in:
Qt Forward Merge Bot 2019-03-29 01:00:40 +01:00
commit f385f6b39f
21 changed files with 479 additions and 145 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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();
}

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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()) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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()),

View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,3 @@
[nameFilters]
msvc-2015
msvc-2017

View File

@ -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<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)

View File

@ -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"
}
}
}
}

View File

@ -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());

View File

@ -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
}
}

View File

@ -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();