Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I9ba374f0c652628b7c84c36893c32b22529e384f
This commit is contained in:
commit
f385f6b39f
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[nameFilters]
|
||||
msvc-2015
|
||||
msvc-2017
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue