diff options
36 files changed, 1299 insertions, 803 deletions
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index 48693dbbf3..7e0de1cd4b 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -281,7 +281,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) ScopedValue values(scope, ctx->callData->args[1]); if (values->asArrayObject()) { ScopedArrayObject array(scope, values); - quint32 size = array->arrayLength(); + quint32 size = array->getLength(); QV4::ScopedValue v(scope); for (quint32 ii = 0; ii < size; ++ii) query.bindValue(ii, engine->toVariant((v = array->getIndexed(ii)), -1)); diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index e8038f1db3..415fdad8e9 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -1015,7 +1015,7 @@ void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *ta void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) { -#if QT_POINTER_SIZE == 8 +#if 0 // QT_POINTER_SIZE == 8 V4IR::Temp *tbase = base->asTemp(); V4IR::Temp *tindex = index->asTemp(); if (tbase && tindex && @@ -1026,9 +1026,10 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR: _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsManaged_Shift), Assembler::ReturnValueRegister); Assembler::Jump notManaged = _as->branch64(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm64(0)); // check whether we have an object with a simple array - Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Managed, flags)); + // ### need to check we have an object first! + Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Object, arrayData) + qOffsetOf(QV4::ArrayData, flags)); _as->load8(managedType, Assembler::ReturnValueRegister); - _as->and32(Assembler::TrustedImm32(QV4::Managed::SimpleArray), Assembler::ReturnValueRegister); + _as->and32(Assembler::TrustedImm32(QV4::ArrayData::SimpleArray), Assembler::ReturnValueRegister); Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); bool needNegativeCheck = false; @@ -1069,12 +1070,12 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR: // get data, ScratchRegister holds index addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase); _as->load64(addr, Assembler::ReturnValueRegister); - Address dataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(Object::ArrayData, length)); + Address dataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, length)); Assembler::Jump outOfRange; if (needNegativeCheck) outOfRange = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); Assembler::Jump outOfRange2 = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, dataLen); - Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(Object::ArrayData, data)); + Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, data)); _as->load64(arrayData, Assembler::ReturnValueRegister); Q_ASSERT(sizeof(Property) == (1<<4)); _as->lshift64(Assembler::TrustedImm32(4), Assembler::ScratchRegister); diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 94983f51e5..a90cea96c2 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -14,6 +14,7 @@ SOURCES += \ $$PWD/qv4managed.cpp \ $$PWD/qv4internalclass.cpp \ $$PWD/qv4sparsearray.cpp \ + $$PWD/qv4arraydata.cpp \ $$PWD/qv4arrayobject.cpp \ $$PWD/qv4argumentsobject.cpp \ $$PWD/qv4booleanobject.cpp \ @@ -58,6 +59,7 @@ HEADERS += \ $$PWD/qv4managed_p.h \ $$PWD/qv4internalclass_p.h \ $$PWD/qv4sparsearray_p.h \ + $$PWD/qv4arraydata_p.h \ $$PWD/qv4arrayobject_p.h \ $$PWD/qv4argumentsobject_p.h \ $$PWD/qv4booleanobject_p.h \ diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index f157796bea..5094bafa66 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -51,7 +51,7 @@ ArgumentsObject::ArgumentsObject(CallContext *context) , context(context) , fullyCreated(false) { - flags &= ~SimpleArray; + setArrayType(ArrayData::Complex); ExecutionEngine *v4 = context->engine; Scope scope(v4); @@ -65,9 +65,8 @@ ArgumentsObject::ArgumentsObject(CallContext *context) memberData[CallerPropertyIndex] = pd; arrayReserve(context->callData->argc); - for (int i = 0; i < context->callData->argc; ++i) - arrayData.data[i].value = context->callData->args[i]; - arrayData.length = context->callData->argc; + arrayData->put(0, context->callData->args, context->callData->argc); + arrayData->setLength(context->callData->argc); fullyCreated = true; } else { Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); @@ -93,18 +92,18 @@ void ArgumentsObject::fullyCreate() uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount); uint argCount = qMin(context->realArgumentCount, context->callData->argc); arrayReserve(argCount); - ensureArrayAttributes(); + arrayData->ensureAttributes(); context->engine->requireArgumentsAccessors(numAccessors); for (uint i = 0; i < (uint)numAccessors; ++i) { mappedArguments.append(context->callData->args[i]); - arrayData.data[i] = context->engine->argumentsAccessors.at(i); - arrayData.attributes[i] = Attr_Accessor; + arrayData->data[i] = context->engine->argumentsAccessors.at(i); + arrayData->setAttributes(i, Attr_Accessor); } - for (uint i = numAccessors; i < argCount; ++i) { - arrayData.data[i] = Property::fromValue(context->callData->args[i]); - arrayData.attributes[i] = Attr_Data; - } - arrayData.length = argCount; + arrayData->setLength(numAccessors); + arrayData->put(numAccessors, context->callData->args + numAccessors, argCount - numAccessors); + for (uint i = numAccessors; i < argCount; ++i) + arrayData->setAttributes(i, Attr_Data); + arrayData->setLength(argCount); fullyCreated = true; } @@ -114,18 +113,17 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const fullyCreate(); Scope scope(ctx); - uint pidx = propertyIndexFromArrayIndex(index); - Property *pd = arrayData.data + pidx; + Property *pd = arrayData->getProperty(index); Property map; PropertyAttributes mapAttrs; bool isMapped = false; if (pd && index < (uint)mappedArguments.size()) - isMapped = arrayData.attributes && arrayData.attributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); + isMapped = arrayData->attributes(index).isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); if (isMapped) { map = *pd; - mapAttrs = arrayData.attributes[pidx]; - arrayData.attributes[pidx] = Attr_Data; + mapAttrs = arrayData->attributes(index); + arrayData->setAttributes(index, Attr_Data); pd->value = mappedArguments.at(index); } @@ -142,7 +140,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const if (attrs.isWritable()) { *pd = map; - arrayData.attributes[pidx] = mapAttrs; + arrayData->setAttributes(index, mapAttrs); } } diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp new file mode 100644 index 0000000000..0ec36bd9db --- /dev/null +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -0,0 +1,563 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4arraydata_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +using namespace QV4; + +const ArrayVTable ArrayData::static_vtbl = +{ + ArrayData::Simple, + ArrayData::freeData, + ArrayData::reserve, + ArrayData::get, + ArrayData::put, + ArrayData::putArray, + ArrayData::del, + ArrayData::setAttribute, + ArrayData::attribute, + ArrayData::push_front, + ArrayData::pop_front, + ArrayData::truncate +}; + +const ArrayVTable SparseArrayData::static_vtbl = +{ + ArrayData::Sparse, + SparseArrayData::freeData, + SparseArrayData::reserve, + SparseArrayData::get, + SparseArrayData::put, + SparseArrayData::putArray, + SparseArrayData::del, + SparseArrayData::setAttribute, + SparseArrayData::attribute, + SparseArrayData::push_front, + SparseArrayData::pop_front, + SparseArrayData::truncate +}; + + +void ArrayData::getHeadRoom(ArrayData *d) +{ + Q_ASSERT(d); + Q_ASSERT(!d->offset); + d->offset = qMax(d->len >> 2, (uint)16); + Property *newArray = new Property[d->offset + d->alloc]; + memcpy(newArray + d->offset, d->data, d->len*sizeof(Property)); + delete [] d->data; + d->data = newArray + d->offset; + if (d->attrs) { + PropertyAttributes *newAttrs = new PropertyAttributes[d->offset + d->alloc]; + memcpy(newAttrs + d->offset, d->attrs, d->len*sizeof(PropertyAttributes)); + delete [] d->attrs; + d->attrs = newAttrs + d->offset; + } +} + +void ArrayData::reserve(ArrayData *d, uint n) +{ + if (n < 8) + n = 8; + if (n <= d->alloc) + return; + + d->alloc = qMax(n, 2*d->alloc); + Property *newArrayData = new Property[d->alloc + d->offset]; + if (d->data) { + memcpy(newArrayData + d->offset, d->data, sizeof(Property)*d->len); + delete [] (d->data - d->offset); + } + d->data = newArrayData + d->offset; + + if (d->attrs) { + PropertyAttributes *newAttrs = new PropertyAttributes[d->alloc]; + memcpy(newAttrs, d->attrs, sizeof(PropertyAttributes)*d->len); + delete [] (d->attrs - d->offset); + + d->attrs = newAttrs; + } +} + +void ArrayData::ensureAttributes() +{ + if (attrs) + return; + + if (type == Simple) + type = Complex; + attrs = new PropertyAttributes[alloc + offset]; + attrs += offset; + for (uint i = 0; i < len; ++i) + attrs[i] = Attr_Data; +} + + +void ArrayData::freeData(ArrayData *d) +{ + delete [] (d->data - d->offset); + if (d->attrs) + delete [] (d->attrs - d->offset); + delete d; +} + +ReturnedValue ArrayData::get(const ArrayData *d, uint index) +{ + if (index >= d->len) + return Primitive::emptyValue().asReturnedValue(); + return d->data[index].value.asReturnedValue(); +} + +bool ArrayData::put(ArrayData *d, uint index, ValueRef value) +{ + Q_ASSERT(!d->attrs || !d->attrs->isAccessor()); + // ### honour attributes + d->data[index].value = value; + return true; +} + +bool ArrayData::del(ArrayData *d, uint index) +{ + if (index >= d->len) + return true; + + if (!d->attrs || d->attrs[index].isConfigurable()) { + d->data[index].value = Primitive::emptyValue(); + if (d->attrs) + d->attrs[index] = Attr_Data; + return true; + } + if (d->data[index].value.isEmpty()) + return true; + return false; +} + +void ArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs) +{ + d->attrs[index] = attrs; +} + +PropertyAttributes ArrayData::attribute(const ArrayData *d, uint index) +{ + return d->attrs[index]; +} + +void ArrayData::push_front(ArrayData *d, SafeValue *values, uint n) +{ + Q_ASSERT(!d->attrs); + for (int i = n - 1; i >= 0; --i) { + if (!d->offset) + ArrayData::getHeadRoom(d); + + --d->offset; + --d->data; + ++d->len; + ++d->alloc; + d->data->value = values[i].asReturnedValue(); + } + +} + +ReturnedValue ArrayData::pop_front(ArrayData *d) +{ + Q_ASSERT(!d->attrs); + if (!d->len) + return Encode::undefined(); + + ReturnedValue v = d->data[0].value.isEmpty() ? Encode::undefined() : d->data[0].value.asReturnedValue(); + ++d->offset; + ++d->data; + --d->len; + --d->alloc; + return v; +} + +uint ArrayData::truncate(ArrayData *d, uint newLen) +{ + if (d->attrs) { + Property *it = d->data + d->len; + const Property *begin = d->data + newLen; + while (--it >= begin) { + if (!it->value.isEmpty() && !d->attrs[it - d->data].isConfigurable()) { + newLen = it - d->data + 1; + break; + } + it->value = Primitive::emptyValue(); + } + } + d->len = newLen; + return newLen; +} + +bool ArrayData::putArray(ArrayData *d, uint index, SafeValue *values, uint n) +{ + if (index + n > d->alloc) + reserve(d, index + n + 1); + for (uint i = d->len; i < index; ++i) + d->data[i].value = Primitive::emptyValue(); + for (uint i = 0; i < n; ++i) + d->data[index + i].value = values[i]; + d->len = qMax(d->len, index + n); + return true; +} + +void SparseArrayData::free(ArrayData *d, uint idx) +{ + Q_ASSERT(d && d->type == ArrayData::Sparse); + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + Property &pd = dd->data[idx]; + pd.value.uint_32 = dd->freeList; + dd->freeList = idx; + if (dd->attrs) + dd->attrs[idx].clear(); +} + + +void SparseArrayData::freeData(ArrayData *d) +{ + delete static_cast<SparseArrayData *>(d)->sparse; + ArrayData::freeData(d); +} + +void SparseArrayData::reserve(ArrayData *d, uint n) +{ + if (n < 8) + n = 8; + if (n <= d->alloc) + return; + + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + // ### FIXME + dd->len = dd->alloc; + dd->alloc = qMax(n, 2*dd->alloc); + Property *newArrayData = new Property[dd->alloc]; + if (dd->data) { + memcpy(newArrayData, dd->data, sizeof(Property)*dd->len); + delete [] dd->data; + } + dd->data = newArrayData; + if (dd->attrs) { + PropertyAttributes *newAttrs = new PropertyAttributes[dd->alloc]; + memcpy(newAttrs, dd->attrs, sizeof(PropertyAttributes)*dd->len); + delete [] dd->attrs; + dd->attrs = newAttrs; + } + for (uint i = dd->freeList; i < dd->alloc; ++i) + dd->data[i].value = Primitive::fromInt32(i + 1); +} + +uint SparseArrayData::allocate(ArrayData *d) +{ + Q_ASSERT(d->type == ArrayData::Sparse); + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + uint idx = dd->freeList; + if (dd->alloc == dd->freeList) + reserve(d, d->alloc + 1); + dd->freeList = dd->data[dd->freeList].value.uint_32; + if (dd->attrs) + dd->attrs[idx].setType(PropertyAttributes::Data); + return idx; +} + +ReturnedValue SparseArrayData::get(const ArrayData *d, uint index) +{ + SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse->findNode(index); + if (!n) + return Primitive::emptyValue().asReturnedValue(); + return d->data[n->value].value.asReturnedValue(); +} + +bool SparseArrayData::put(ArrayData *d, uint index, ValueRef value) +{ + // ### honour attributes + SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index); + if (n->value == UINT_MAX) + n->value = allocate(d); + d->data[n->value].value = value; + return true; +} + +bool SparseArrayData::del(ArrayData *d, uint index) +{ + SparseArrayData *dd = static_cast<SparseArrayData *>(d); + SparseArrayNode *n = dd->sparse->findNode(index); + if (!n) + return true; + + uint pidx = n->value; + Q_ASSERT(!dd->data[pidx].value.isEmpty()); + + if (!dd->attrs || dd->attrs[pidx].isConfigurable()) { + d->data[pidx].value.int_32 = static_cast<SparseArrayData *>(d)->freeList; + static_cast<SparseArrayData *>(d)->freeList = pidx; + static_cast<SparseArrayData *>(d)->sparse->erase(n); + return true; + } + return false; +} + +void SparseArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs) +{ + SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index); + if (n->value == UINT_MAX) + n->value = allocate(d); + d->attrs[n->value] = attrs; +} + +PropertyAttributes SparseArrayData::attribute(const ArrayData *d, uint index) +{ + SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse->insert(index); + if (!n) + return PropertyAttributes(); + return d->attrs[n->value]; +} + +void SparseArrayData::push_front(ArrayData *d, SafeValue *values, uint n) +{ + Q_ASSERT(!d->attrs); + for (int i = n - 1; i >= 0; --i) { + uint idx = allocate(d); + d->data[idx].value = values[i]; + static_cast<SparseArrayData *>(d)->sparse->push_front(idx); + } +} + +ReturnedValue SparseArrayData::pop_front(ArrayData *d) +{ + Q_ASSERT(!d->attrs); + uint idx = static_cast<SparseArrayData *>(d)->sparse->pop_front(); + ReturnedValue v; + if (idx != UINT_MAX) { + v = d->data[idx].value.asReturnedValue(); + SparseArrayData::free(d, idx); + } else { + v = Encode::undefined(); + } + return v; +} + +uint SparseArrayData::truncate(ArrayData *d, uint newLen) +{ + SparseArrayNode *begin = static_cast<SparseArrayData *>(d)->sparse->lowerBound(newLen); + if (begin != static_cast<SparseArrayData *>(d)->sparse->end()) { + SparseArrayNode *it = static_cast<SparseArrayData *>(d)->sparse->end()->previousNode(); + while (1) { + Property &pd = d->data[it->value]; + if (d->attrs) { + if (!d->attrs[it->value].isConfigurable()) { + newLen = it->key() + 1; + break; + } + } + pd.value.tag = Value::Empty_Type; + pd.value.int_32 = static_cast<SparseArrayData *>(d)->freeList; + static_cast<SparseArrayData *>(d)->freeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + static_cast<SparseArrayData *>(d)->sparse->erase(it); + if (brk) + break; + it = prev; + } + } + return newLen; +} + +bool SparseArrayData::putArray(ArrayData *d, uint index, SafeValue *values, uint n) +{ + for (uint i = 0; i < n; ++i) + put(d, index + i, values[i]); + d->len = qMax(d->len, index + n); + return true; +} + + +uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n) +{ + ArrayData *d = o->arrayData; + if (!n) + return d->len; + + const ArrayData *other = otherObj->arrayData; + + if (other->isSparse()) { + o->initSparseArray(); + d = o->arrayData; + } + + uint oldSize = d->len; + + // ### copy attributes as well! + if (d->type == ArrayData::Sparse) { + if (other->isSparse()) { + for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin(); + it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode()) + // ### accessor properties + o->arraySet(d->len + it->key(), other->data[it->value].value); + } else { + d->vtable->reserve(d, oldSize + n); + memcpy(d->data + oldSize, other->data, n*sizeof(Property)); + if (d->attrs) + std::fill(d->attrs + oldSize, d->attrs + oldSize + n, PropertyAttributes(Attr_Data)); + for (uint i = 0; i < n; ++i) { + SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(d->len + i); + n->value = oldSize + i; + } + } + } else if (other->length()) { + d->vtable->reserve(d, oldSize + other->length()); + if (oldSize > d->len) { + for (uint i = d->len; i < oldSize; ++i) + d->data[i].value = Primitive::emptyValue(); + } + if (other->attrs) { + for (uint i = 0; i < other->len; ++i) { + bool exists; + d->data[oldSize + i].value = const_cast<ArrayObject *>(otherObj)->getIndexed(i, &exists); + d->len = oldSize + i + 1; + o->arrayData->setAttributes(oldSize + i, Attr_Data); + if (!exists) + d->data[oldSize + i].value = Primitive::emptyValue(); + } + } else { + d->len = oldSize + other->len; + memcpy(d->data + oldSize, other->data, other->len*sizeof(Property)); + if (d->attrs) + std::fill(d->attrs + oldSize, d->attrs + oldSize + other->len, PropertyAttributes(Attr_Data)); + } + } + + return oldSize + n; +} + +Property *ArrayData::insert(Object *o, uint index) +{ + Property *pd; + if (o->arrayData->type != ArrayData::Sparse && (index < 0x1000 || index < o->arrayData->len + (o->arrayData->len >> 2))) { + if (index >= o->arrayData->alloc) + o->arrayReserve(index + 1); + if (index >= o->arrayData->len) { + // mark possible hole in the array + for (uint i = o->arrayData->len; i < index; ++i) + o->arrayData->data[i].value = Primitive::emptyValue(); + o->arrayData->len = index + 1; + } + pd = o->arrayData->data + index; + } else { + o->initSparseArray(); + SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData)->sparse->insert(index); + if (n->value == UINT_MAX) + n->value = SparseArrayData::allocate(o->arrayData); + pd = o->arrayData->data + n->value; + } + return pd; +} + +void ArrayData::markObjects(ExecutionEngine *e) +{ + if (type == ArrayData::Simple) { + for (uint i = 0; i < len; ++i) + data[i].value.mark(e); + return; + } else { + for (uint i = 0; i < len; ++i) { + const Property &pd = data[i]; + if (attrs && attrs[i].isAccessor()) { + if (pd.getter()) + pd.getter()->mark(e); + if (pd.setter()) + pd.setter()->mark(e); + } else { + pd.value.mark(e); + } + } + } + +} + +void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len) +{ + ArrayData *d = thisObject->arrayData; + if (!d || !d->len) + return; + + if (d->type == ArrayData::Sparse) { + context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays")); + return; + } + + if (len > d->len) + len = d->len; + + // The spec says the sorting goes through a series of get,put and delete operations. + // this implies that the attributes don't get sorted around. + // behavior of accessor properties is implementation defined. We simply turn them all + // into data properties and then sort. This is in line with the sentence above. + if (d->attrs) { + for (uint i = 0; i < len; i++) { + if (d->data[i].value.isEmpty()) { + while (--len > i) + if (!d->data[len].value.isEmpty()) + break; + d->data[i].value = thisObject->getValue(d->data + len, d->attrs ? d->attrs[len] : Attr_Data); + if (d->attrs) + d->attrs[i] = Attr_Data; + d->data[len].value = Primitive::emptyValue(); + } else if (d->attrs && d->attrs[i].isAccessor()) { + d->data[i].value = thisObject->getValue(d->data + i, d->attrs[i]); + d->attrs[i] = Attr_Data; + } + } + } + + if (!(comparefn->isUndefined() || comparefn->asObject())) { + context->throwTypeError(); + return; + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + + if (!len) + return; + Property *begin = d->data; + std::sort(begin, begin + len, lessThan); +} diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h new file mode 100644 index 0000000000..89628cde0e --- /dev/null +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARRAYDATA_H +#define QV4ARRAYDATA_H + +#include "qv4global_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4sparsearray_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArrayData; + +struct ArrayVTable +{ + uint type; + void (*freeData)(ArrayData *d); + void (*reserve)(ArrayData *d, uint n); + ReturnedValue (*get)(const ArrayData *d, uint index); + bool (*put)(ArrayData *d, uint index, ValueRef value); + bool (*putArray)(ArrayData *d, uint index, SafeValue *values, uint n); + bool (*del)(ArrayData *d, uint index); + void (*setAttribute)(ArrayData *d, uint index, PropertyAttributes attrs); + PropertyAttributes (*attribute)(const ArrayData *d, uint index); + void (*push_front)(ArrayData *d, SafeValue *values, uint n); + ReturnedValue (*pop_front)(ArrayData *d); + uint (*truncate)(ArrayData *d, uint newLen); +}; + + +struct Q_QML_EXPORT ArrayData +{ + ArrayData() + : vtable(&static_vtbl) + , offset(0) + , len(0) + , alloc(0) + , type(0) + , attrs(0) + , data(0) + { + } + + enum Type { + Simple = 0, + Complex = 1, + Sparse = 2, + Custom = 3 + }; + + const ArrayVTable *vtable; + uint offset; + uint len; + uint alloc; + uint type; + PropertyAttributes *attrs; + Property *data; + + bool isSparse() const { return this && type == Sparse; } + + uint length() const { + return this ? len : 0; + } + void setLength(uint l) { + Q_ASSERT(this); + len = l; + } + + bool hasAttributes() const { + return this && attrs; + } + void ensureAttributes(); + PropertyAttributes attributes(int i) const { + Q_ASSERT(this); + return attrs ? vtable->attribute(this, i) : Attr_Data; + } + void setAttributes(uint i, PropertyAttributes a) { + Q_ASSERT(this); + if (attrs || a != Attr_Data) { + ensureAttributes(); + a.resolve(); + vtable->setAttribute(this, i, a); + } + } + + bool isEmpty(uint i) const { + if (!this) + return true; + return (vtable->get(this, i) == Primitive::emptyValue().asReturnedValue()); + } + + + inline void free() { + vtable->freeData(this); + } + + inline void push_front(SafeValue *values, uint nValues) { + vtable->push_front(this, values, nValues); + } + inline ReturnedValue pop_front() { + return vtable->pop_front(this); + } + inline uint push_back(uint l, uint n, SafeValue *values) { + vtable->putArray(this, l, values, n); + return len; + } + inline bool deleteIndex(uint index) { + return vtable->del(this, index); + } + inline uint truncate(uint newLen) { + if (!this || len < newLen) + return newLen; + return vtable->truncate(this, newLen); + } + bool put(uint index, ValueRef value) { + return vtable->put(this, index, value); + } + bool put(uint index, SafeValue *values, uint n) { + return vtable->putArray(this, index, values, n); + } + ReturnedValue get(uint i) const { + if (!this) + return Primitive::emptyValue().asReturnedValue(); + return vtable->get(this, i); + } + inline Property *getProperty(uint index) const; + + + + static void sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint dataLen); + static uint append(Object *o, const ArrayObject *otherObj, uint n); + static Property *insert(Object *o, uint index); + void markObjects(ExecutionEngine *e); + + static void getHeadRoom(ArrayData *d); + static void reserve(ArrayData *d, uint n); + + static void freeData(ArrayData *d); + static ReturnedValue get(const ArrayData *d, uint index); + static bool put(ArrayData *d, uint index, ValueRef value); + static bool putArray(ArrayData *d, uint index, SafeValue *values, uint n); + static bool del(ArrayData *d, uint index); + static void setAttribute(ArrayData *d, uint index, PropertyAttributes attrs); + static PropertyAttributes attribute(const ArrayData *d, uint index); + static void push_front(ArrayData *d, SafeValue *values, uint n); + static ReturnedValue pop_front(ArrayData *d); + static uint truncate(ArrayData *d, uint newLen); + + static const ArrayVTable static_vtbl; +}; + +struct Q_QML_EXPORT SparseArrayData : public ArrayData +{ + SparseArrayData() + : freeList(0) + , sparse(0) + { vtable = &static_vtbl; } + + uint freeList; + SparseArray *sparse; + + static uint allocate(ArrayData *d); + static void free(ArrayData *d, uint idx); + + static void freeData(ArrayData *d); + static void reserve(ArrayData *d, uint n); + static ReturnedValue get(const ArrayData *d, uint index); + static bool put(ArrayData *d, uint index, ValueRef value); + static bool putArray(ArrayData *d, uint index, SafeValue *values, uint n); + static bool del(ArrayData *d, uint index); + static void setAttribute(ArrayData *d, uint index, PropertyAttributes attrs); + static PropertyAttributes attribute(const ArrayData *d, uint index); + static void push_front(ArrayData *d, SafeValue *values, uint n); + static ReturnedValue pop_front(ArrayData *d); + static uint truncate(ArrayData *d, uint newLen); + + static const ArrayVTable static_vtbl; +}; + + +inline Property *ArrayData::getProperty(uint index) const +{ + if (!this) + return 0; + if (type != Sparse) { + if (index >= len) + return 0; + return data + index; + } else { + SparseArrayNode *n = static_cast<const SparseArrayData *>(this)->sparse->findNode(index); + if (!n) + return 0; + return data + n->value; + } +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 0fc7f2ddb9..eb8a5301de 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -72,9 +72,8 @@ ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) } else { len = callData->argc; a->arrayReserve(len); - for (unsigned int i = 0; i < len; ++i) - a->arrayData.data[i].value = callData->args[i]; - a->arrayData.length = len; + a->arrayData->put(0, callData->args, len); + a->arrayData->setLength(len); } a->setArrayLengthUnchecked(len); @@ -122,15 +121,6 @@ void ArrayPrototype::init(ExecutionEngine *engine, ObjectRef ctor) defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); } -uint ArrayPrototype::getLength(ExecutionContext *ctx, ObjectRef o) -{ - if (o->isArrayObject()) - return o->arrayLength(); - Scope scope(ctx); - ScopedValue v(scope, o->get(ctx->engine->id_length)); - return v->toUInt32(); -} - ReturnedValue ArrayPrototype::method_isArray(CallContext *ctx) { bool isArray = ctx->callData->argc && ctx->callData->args[0].asArrayObject(); @@ -180,15 +170,17 @@ ReturnedValue ArrayPrototype::method_concat(CallContext *ctx) eltAsObj = ctx->callData->args[i]; elt = ctx->callData->args[i]; if (elt) { - result->arrayConcat(elt.getPointer()); + uint n = elt->getLength(); + uint newLen = ArrayData::append(result.getPointer(), elt.getPointer(), n); + result->setArrayLengthUnchecked(newLen); } else if (eltAsObj && eltAsObj->isListType()) { - const uint startIndex = getLength(ctx, result); - for (int i = 0, len = getLength(ctx, eltAsObj); i < len; ++i) { + const uint startIndex = result->getLength(); + for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { entry = eltAsObj->getIndexed(i); result->putIndexed(startIndex + i, entry); } } else { - result->arraySet(getLength(ctx, result), ctx->callData->args[i]); + result->arraySet(result->getLength(), ctx->callData->args[i]); } } @@ -218,7 +210,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) // ### FIXME if (ArrayObject *a = self->asArrayObject()) { ScopedValue e(scope); - for (uint i = 0; i < a->arrayLength(); ++i) { + for (uint i = 0; i < a->getLength(); ++i) { if (i) R += r4; @@ -260,7 +252,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); if (!len) { if (!instance->isArrayObject()) @@ -288,7 +280,10 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + + instance->arrayCreate(); + + uint len = instance->getLength(); if (len + ctx->callData->argc < len) { // ughh... @@ -308,21 +303,8 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) return Encode(newLen); } - if (!instance->protoHasArray() && instance->arrayData.length <= len) { - for (int i = 0; i < ctx->callData->argc; ++i) { - if (!instance->arrayData.sparse) { - if (len >= instance->arrayData.alloc) - instance->arrayReserve(len + 1); - instance->arrayData.data[len].value = ctx->callData->args[i]; - if (instance->arrayData.attributes) - instance->arrayData.attributes[len] = Attr_Data; - instance->arrayData.length = len + 1; - } else { - uint j = instance->allocArrayValue(ctx->callData->args[i]); - instance->arrayData.sparse->push_back(j, len); - } - ++len; - } + if (!instance->protoHasArray() && instance->arrayData->length() <= len) { + len = instance->arrayData->push_back(len, ctx->callData->argc, ctx->callData->args); } else { for (int i = 0; i < ctx->callData->argc; ++i) instance->putIndexed(len + i, ctx->callData->args[i]); @@ -342,7 +324,7 @@ ReturnedValue ArrayPrototype::method_reverse(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint length = getLength(ctx, instance); + uint length = instance->getLength(); int lo = 0, hi = length - 1; @@ -374,7 +356,10 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + + instance->arrayCreate(); + + uint len = instance->getLength(); if (!len) { if (!instance->isArrayObject()) @@ -382,28 +367,14 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) return Encode::undefined(); } - Property *front = 0; - uint pidx = instance->propertyIndexFromArrayIndex(0); - if (pidx < UINT_MAX && !instance->arrayData.data[pidx].value.isEmpty()) - front = instance->arrayData.data + pidx; + ScopedValue result(scope); - ScopedValue result(scope, front ? instance->getValue(front, instance->arrayData.attributes ? instance->arrayData.attributes[pidx] : Attr_Data) : Encode::undefined()); - - if (!instance->protoHasArray() && instance->arrayData.length <= len) { - if (!instance->arrayData.sparse) { - if (instance->arrayData.length) { - ++instance->arrayData.offset; - ++instance->arrayData.data; - --instance->arrayData.length; - --instance->arrayData.alloc; - if (instance->arrayData.attributes) - ++instance->arrayData.attributes; - } - } else { - uint idx = instance->arrayData.sparse->pop_front(); - instance->freeArrayValue(idx); - } + if (!instance->protoHasArray() && !instance->arrayData->hasAttributes() && instance->arrayData->length() <= len) { + result = instance->arrayData->pop_front(); } else { + result = instance->getIndexed(0); + if (scope.hasException()) + return Encode::undefined(); ScopedValue v(scope); // do it the slow way for (uint k = 1; k < len; ++k) { @@ -415,6 +386,8 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) instance->putIndexed(k - 1, v); else instance->deleteIndexedProperty(k - 1); + if (scope.hasException()) + return Encode::undefined(); } instance->deleteIndexedProperty(len - 1); if (scope.hasException()) @@ -436,7 +409,7 @@ ReturnedValue ArrayPrototype::method_slice(CallContext *ctx) return Encode::undefined(); Scoped<ArrayObject> result(scope, ctx->engine->newArrayObject()); - uint len = getLength(ctx, o); + uint len = o->getLength(); double s = ScopedValue(scope, ctx->argument(0))->toInteger(); uint start; if (s < 0) @@ -477,10 +450,10 @@ ReturnedValue ArrayPrototype::method_sort(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); ScopedValue comparefn(scope, ctx->argument(0)); - instance->arraySort(ctx, instance, comparefn, len); + ArrayData::sort(ctx, instance, comparefn, len); return ctx->callData->thisObject.asReturnedValue(); } @@ -490,7 +463,7 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<ArrayObject> newArray(scope, ctx->engine->newArrayObject()); @@ -504,17 +477,21 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) uint deleteCount = (uint)qMin(qMax(ScopedValue(scope, ctx->argument(1))->toInteger(), 0.), (double)(len - start)); newArray->arrayReserve(deleteCount); + ScopedValue v(scope); for (uint i = 0; i < deleteCount; ++i) { - newArray->arrayData.data[i].value = instance->getIndexed(start + i); + bool exists; + v = instance->getIndexed(start + i, &exists); if (scope.hasException()) return Encode::undefined(); - newArray->arrayData.length = i + 1; + if (exists) { + newArray->arrayData->put(i, v); + newArray->arrayData->setLength(i + 1); + } } newArray->setArrayLengthUnchecked(deleteCount); uint itemCount = ctx->callData->argc < 2 ? 0 : ctx->callData->argc - 2; - ScopedValue v(scope); if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; @@ -568,32 +545,15 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); - ScopedValue v(scope); - if (!instance->protoHasArray() && instance->arrayData.length <= len) { - for (int i = ctx->callData->argc - 1; i >= 0; --i) { - v = ctx->argument(i); - - if (!instance->arrayData.sparse) { - if (!instance->arrayData.offset) - instance->getArrayHeadRoom(); - - --instance->arrayData.offset; - --instance->arrayData.data; - ++instance->arrayData.length; - ++instance->arrayData.alloc; - if (instance->arrayData.attributes) { - --instance->arrayData.attributes; - *instance->arrayData.attributes = Attr_Data; - } - instance->arrayData.data->value = v.asReturnedValue(); - } else { - uint idx = instance->allocArrayValue(v); - instance->arrayData.sparse->push_front(idx); - } - } + instance->arrayCreate(); + + uint len = instance->getLength(); + + if (!instance->protoHasArray() && !instance->arrayData->hasAttributes() && instance->arrayData->length() <= len) { + instance->arrayData->push_front(ctx->callData->args, ctx->callData->argc); } else { + ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; v = instance->getIndexed(k - 1, &exists); @@ -622,7 +582,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); if (!len) return Encode(-1); @@ -651,7 +611,48 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) return Encode(-1); } - return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance.getPointer()); + ScopedValue value(scope); + + if ((instance->arrayData && instance->arrayType() != ArrayData::Simple) || instance->protoHasArray()) { + // lets be safe and slow + for (uint i = fromIndex; i < len; ++i) { + bool exists; + value = instance->getIndexed(i, &exists); + if (scope.hasException()) + return Encode::undefined(); + if (exists && __qmljs_strict_equal(value, searchValue)) + return Encode(i); + } + } else if (!instance->arrayData) { + return Encode(-1); + } else if (instance->arrayType() == ArrayData::Sparse) { + for (SparseArrayNode *n = static_cast<SparseArrayData *>(instance->arrayData)->sparse->lowerBound(fromIndex); + n != static_cast<SparseArrayData *>(instance->arrayData)->sparse->end() && n->key() < len; n = n->nextNode()) { + value = instance->getValue(instance->arrayData->data + n->value, + instance->arrayData->attrs ? instance->arrayData->attrs[n->value] : Attr_Data); + if (scope.hasException()) + return Encode::undefined(); + if (__qmljs_strict_equal(value, searchValue)) + return Encode(n->key()); + } + } else { + if (len > instance->arrayData->length()) + len = instance->arrayData->length(); + Property *pd = instance->arrayData->data; + Property *end = pd + len; + pd += fromIndex; + while (pd < end) { + if (!pd->value.isEmpty()) { + value = instance->getValue(pd, instance->arrayData->attributes(pd - instance->arrayData->data)); + if (scope.hasException()) + return Encode::undefined(); + if (__qmljs_strict_equal(value, searchValue)) + return Encode((uint)(pd - instance->arrayData->data)); + } + ++pd; + } + } + return Encode(-1); } ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx) @@ -661,7 +662,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx) ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx)); if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); if (!len) return Encode(-1); @@ -707,7 +708,7 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -741,7 +742,7 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -775,7 +776,7 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -806,7 +807,7 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -843,7 +844,7 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -884,7 +885,7 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) @@ -934,7 +935,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx) if (!instance) return Encode::undefined(); - uint len = getLength(ctx, instance); + uint len = instance->getLength(); Scoped<FunctionObject> callback(scope, ctx->argument(0)); if (!callback) diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 7e809f9064..641be86e2c 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -64,8 +64,6 @@ struct ArrayPrototype: ArrayObject void init(ExecutionEngine *engine, ObjectRef ctor); - static uint getLength(ExecutionContext *ctx, ObjectRef o); - static ReturnedValue method_isArray(CallContext *ctx); static ReturnedValue method_toString(CallContext *ctx); static ReturnedValue method_toLocaleString(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 35ed8fe0f3..27c3a31d95 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -77,6 +77,7 @@ const ManagedVTable ExecutionContext::static_vtbl = 0, isEqualTo, 0, + 0, "ExecutionContext", }; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 4538884bce..10b2590047 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -343,19 +343,19 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) if (!arg->isNullOrUndefined()) return ctx->throwTypeError(); } else { - len = ArrayPrototype::getLength(ctx, arr); + len = arr->getLength(); } ScopedCallData callData(scope, len); if (len) { - if (!(arr->flags & SimpleArray) || arr->protoHasArray() || arr->hasAccessorProperty) { + if (arr->arrayType() != ArrayData::Simple || arr->protoHasArray() || arr->hasAccessorProperty) { for (quint32 i = 0; i < len; ++i) callData->args[i] = arr->getIndexed(i); } else { - int alen = qMin(len, arr->arrayData.length); + int alen = qMin(len, arr->arrayData->length()); for (int i = 0; i < alen; ++i) - callData->args[i] = arr->arrayData.data[i].value; + callData->args[i] = arr->arrayData->data[i].value; for (quint32 i = alen; i < len; ++i) callData->args[i] = Primitive::undefinedValue(); } diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 787e5300f5..65f053e92f 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -339,7 +339,7 @@ ReturnedValue JsonParser::parseArray() } } - DEBUG << "size =" << array->arrayLength(); + DEBUG << "size =" << array->getLength(); END; --nestingLevel; @@ -855,7 +855,7 @@ QString Stringify::JA(ArrayObjectRef a) indent += gap; QStringList partial; - uint len = a->arrayLength(); + uint len = a->getLength(); ScopedValue v(scope); for (uint i = 0; i < len; ++i) { bool exists; @@ -925,7 +925,7 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) if (o) { stringify.replacerFunction = o->asFunctionObject(); if (o->isArrayObject()) { - uint arrayLen = o->arrayLength(); + uint arrayLen = o->getLength(); ScopedValue v(scope); for (uint i = 0; i < arrayLen; ++i) { v = o->getIndexed(i); @@ -1057,9 +1057,10 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso int size = array.size(); Scoped<ArrayObject> a(scope, engine->newArrayObject()); a->arrayReserve(size); + ScopedValue v(scope); for (int i = 0; i < size; i++) { - a->arrayData.data[i].value = fromJsonValue(engine, array.at(i)); - a->arrayData.length = i + 1; + a->arrayData->put(i, (v = fromJsonValue(engine, array.at(i)))); + a->arrayData->setLength(i + 1); } a->setArrayLengthUnchecked(size); return a.asReturnedValue(); @@ -1083,7 +1084,7 @@ QJsonArray JsonObject::toJsonArray(ArrayObjectRef a, V4ObjectSet &visitedObjects visitedObjects.insert(a); ScopedValue v(scope); - quint32 length = a->arrayLength(); + quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); if (v->asFunctionObject()) diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index a870cdac61..66dc7b7e4a 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -316,6 +316,15 @@ ReturnedValue Lookup::stringLengthGetter(Lookup *l, const ValueRef object) return getterGeneric(l, object); } +ReturnedValue Lookup::arrayLengthGetter(Lookup *l, const ValueRef object) +{ + if (ArrayObject *a = object->asArrayObject()) + return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue(); + + l->getter = getterGeneric; + return getterGeneric(l, object); +} + ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx) { diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 9966d36604..93c2e2156e 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -85,6 +85,7 @@ struct Lookup { static ReturnedValue primitiveGetterAccessor0(Lookup *l, const ValueRef object); static ReturnedValue primitiveGetterAccessor1(Lookup *l, const ValueRef object); static ReturnedValue stringLengthGetter(Lookup *l, const ValueRef object); + static ReturnedValue arrayLengthGetter(Lookup *l, const ValueRef object); static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionContext *ctx); static ReturnedValue globalGetter0(Lookup *l, ExecutionContext *ctx); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 47f9195322..c3dec2535b 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -71,6 +71,7 @@ const ManagedVTable Managed::static_vtbl = 0, isEqualTo, 0, + 0, "Managed", }; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 124392197f..932dbe818c 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -106,6 +106,7 @@ struct ManagedVTable ReturnedValue (*getLookup)(Managed *m, Lookup *l); void (*setLookup)(Managed *m, Lookup *l, const ValueRef v); bool (*isEqualTo)(Managed *m, Managed *other); + uint (*getLength)(const Managed *m); Property *(*advanceIterator)(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); const char *className; }; @@ -136,6 +137,7 @@ const QV4::ManagedVTable classname::static_vtbl = \ getLookup, \ setLookup, \ isEqualTo, \ + getLength, \ advanceIterator, \ #classname \ } @@ -166,6 +168,7 @@ const QV4::ManagedVTable classname::static_vtbl = \ getLookup, \ setLookup, \ isEqualTo, \ + getLength, \ advanceIterator, \ #name \ } @@ -196,6 +199,7 @@ const QV4::ManagedVTable classname::static_vtbl = \ getLookup, \ setLookup, \ isEqualTo, \ + getLength, \ advanceIterator, \ #classname \ } @@ -327,6 +331,7 @@ public: bool isEqualTo(Managed *other) { return internalClass->vtable->isEqualTo(this, other); } + uint getLength() const { return internalClass->vtable->getLength(this); } Property *advanceIterator(ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); static void destroy(Managed *that) { that->_data = 0; } @@ -335,16 +340,13 @@ public: static ReturnedValue getLookup(Managed *m, Lookup *); static void setLookup(Managed *m, Lookup *l, const ValueRef v); static bool isEqualTo(Managed *m, Managed *other); + static uint getLength(const Managed *) { return 0; } ReturnedValue asReturnedValue() { return Value::fromManaged(this).asReturnedValue(); } InternalClass *internalClass; - enum { - SimpleArray = 1 - }; - union { uint _data; struct { @@ -358,7 +360,7 @@ public: uchar hasAccessorProperty : 1; uchar _type; mutable uchar subtype; - uchar flags; + uchar _flags; }; }; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index fb56d623fd..142eae82fd 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -73,7 +73,6 @@ Object::Object(ExecutionEngine *engine) : Managed(engine->objectClass) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) { - flags = SimpleArray; } Object::Object(InternalClass *ic) @@ -81,7 +80,6 @@ Object::Object(InternalClass *ic) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) { Q_ASSERT(internalClass->vtable && internalClass->vtable != &Managed::static_vtbl); - flags = SimpleArray; if (internalClass->size >= memberDataAlloc) { memberDataAlloc = internalClass->size; @@ -93,10 +91,8 @@ Object::~Object() { if (memberData != inlineProperties) delete [] memberData; - delete [] (arrayData.data - (arrayData.sparse ? 0 : arrayData.offset)); - if (arrayData.attributes) - delete [] (arrayData.attributes - (arrayData.sparse ? 0 : arrayData.offset)); - delete arrayData.sparse; + if (arrayData) + arrayData->free(); _data = 0; } @@ -252,23 +248,8 @@ void Object::markObjects(Managed *that, ExecutionEngine *e) } } } - if (o->flags & SimpleArray) { - for (uint i = 0; i < o->arrayData.length; ++i) - o->arrayData.data[i].value.mark(e); - return; - } else { - for (uint i = 0; i < o->arrayData.length; ++i) { - const Property &pd = o->arrayData.data[i]; - if (o->arrayData.attributes && o->arrayData.attributes[i].isAccessor()) { - if (pd.getter()) - pd.getter()->mark(e); - if (pd.setter()) - pd.setter()->mark(e); - } else { - pd.value.mark(e); - } - } - } + if (o->arrayData) + o->arrayData->markObjects(e); } void Object::ensureMemberIndex(uint idx) @@ -318,12 +299,11 @@ Property *Object::__getOwnProperty__(const StringRef name, PropertyAttributes *a Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - Property *p = arrayData.data + pidx; - if (!p->value.isEmpty() && !(arrayData.attributes && arrayData.attributes[pidx].isGeneric())) { + Property *p = arrayData->getProperty(index); + if (p) { + if (!p->value.isEmpty()) { if (attrs) - *attrs = arrayData.attributes ? arrayData.attributes[pidx] : PropertyAttributes(Attr_Data); + *attrs = arrayData->attributes(index); return p; } } @@ -366,12 +346,11 @@ Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attr { const Object *o = this; while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - Property *p = o->arrayData.data + pidx; + Property *p = o->arrayData->getProperty(index); + if (p) { if (!p->value.isEmpty()) { if (attrs) - *attrs = o->arrayData.attributes ? o->arrayData.attributes[pidx] : PropertyAttributes(Attr_Data); + *attrs = o->arrayData->attributes(index); return p; } } @@ -467,13 +446,9 @@ PropertyAttributes Object::query(const Managed *m, StringRef name) PropertyAttributes Object::queryIndexed(const Managed *m, uint index) { const Object *o = static_cast<const Object *>(m); - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (o->arrayData.attributes) - return o->arrayData.attributes[pidx]; - if (!o->arrayData.data[pidx].value.isEmpty()) - return Attr_Data; - } + if (o->arrayData->get(index) != Primitive::emptyValue().asReturnedValue()) + return o->arrayData->attributes(index); + if (o->isStringObject()) { Property *p = static_cast<const StringObject *>(o)->getIndex(index); if (p) @@ -574,41 +549,42 @@ Property *Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name name = (String *)0; *index = UINT_MAX; - if (!it->arrayIndex) - it->arrayNode = o->sparseArrayBegin(); - - // sparse arrays - if (it->arrayNode) { - while (it->arrayNode != o->sparseArrayEnd()) { - int k = it->arrayNode->key(); - uint pidx = it->arrayNode->value; - Property *p = o->arrayData.data + pidx; - it->arrayNode = it->arrayNode->nextNode(); - PropertyAttributes a = o->arrayData.attributes ? o->arrayData.attributes[pidx] : PropertyAttributes(Attr_Data); - if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - it->arrayIndex = k + 1; - *index = k; + if (o->arrayData) { + if (!it->arrayIndex) + it->arrayNode = o->sparseBegin(); + + // sparse arrays + if (it->arrayNode) { + while (it->arrayNode != o->sparseEnd()) { + int k = it->arrayNode->key(); + uint pidx = it->arrayNode->value; + Property *p = o->arrayData->data + pidx; + it->arrayNode = it->arrayNode->nextNode(); + PropertyAttributes a = o->arrayData->attributes(k); + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + it->arrayIndex = k + 1; + *index = k; + if (attrs) + *attrs = a; + return p; + } + } + it->arrayNode = 0; + it->arrayIndex = UINT_MAX; + } + // dense arrays + while (it->arrayIndex < o->arrayData->length()) { + Property *p = o->arrayData->data + it->arrayIndex; + PropertyAttributes a = o->arrayData->attributes(it->arrayIndex); + ++it->arrayIndex; + if (!p->value.isEmpty() + && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { + *index = it->arrayIndex - 1; if (attrs) *attrs = a; return p; } } - it->arrayNode = 0; - it->arrayIndex = UINT_MAX; - } - // dense arrays - while (it->arrayIndex < o->arrayData.length) { - uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex); - Property *p = o->arrayData.data + pidx; - PropertyAttributes a = o->arrayData.attributes ? o->arrayData.attributes[pidx] : PropertyAttributes(Attr_Data); - ++it->arrayIndex; - if (!p->value.isEmpty() - && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { - *index = it->arrayIndex - 1; - if (attrs) - *attrs = a; - return p; - } } while (it->memberIndex < o->internalClass->size) { @@ -658,15 +634,14 @@ ReturnedValue Object::internalGet(const StringRef name, bool *hasProperty) ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) { Property *pd = 0; - PropertyAttributes attrs = Attr_Data; + PropertyAttributes attrs; Object *o = this; while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (!o->arrayData.data[pidx].value.isEmpty()) { - pd = o->arrayData.data + pidx; - if (o->arrayData.attributes) - attrs = o->arrayData.attributes[pidx]; + Property *p = o->arrayData->getProperty(index); + if (p) { + if (!p->value.isEmpty()) { + pd = p; + attrs = o->arrayData->attributes(index); break; } } @@ -788,10 +763,10 @@ void Object::internalPutIndexed(uint index, const ValueRef value) Property *pd = 0; PropertyAttributes attrs; - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX && !arrayData.data[pidx].value.isEmpty()) { - pd = arrayData.data + pidx; - attrs = arrayData.attributes ? arrayData.attributes[pidx] : PropertyAttributes(Attr_Data); + Property *p = arrayData->getProperty(index); + if (p && !p->value.isEmpty()) { + pd = p; + attrs = arrayData->attributes(index); } if (!pd && isStringObject()) { @@ -883,23 +858,9 @@ bool Object::internalDeleteIndexedProperty(uint index) if (internalClass->engine->hasException) return false; - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx == UINT_MAX) - return true; - if (arrayData.data[pidx].value.isEmpty()) + if (!arrayData || arrayData->deleteIndex(index)) return true; - if (!arrayData.attributes || arrayData.attributes[pidx].isConfigurable()) { - arrayData.data[pidx].value = Primitive::emptyValue(); - if (arrayData.attributes) - arrayData.attributes[pidx].clear(); - if (arrayData.sparse) { - arrayData.data[pidx].value.int_32 = arrayData.freeList; - arrayData.freeList = pidx; - } - return true; - } - if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); return false; @@ -974,7 +935,7 @@ reject: bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) { // 15.4.5.1, 4b - if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + if (isArrayObject() && index >= getLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) goto reject; if (ArgumentsObject::isNonStrictArgumentsObject(this)) @@ -993,9 +954,9 @@ bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Propert // Clause 1 { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX && !arrayData.data[pidx].value.isEmpty()) - current = arrayData.data + pidx; + Property *p = arrayData->getProperty(index); + if (p && !p->value.isEmpty()) + current = p; if (!current && isStringObject()) current = static_cast<StringObject *>(this)->getIndex(index); } @@ -1005,9 +966,15 @@ bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Propert if (!extensible) goto reject; // clause 4 - Property *pd = arrayInsert(index, attrs); - *pd = p; - pd->fullyPopulated(&attrs); + Property pp(p); + pp.fullyPopulated(&attrs); + if (attrs == Attr_Data) { + Scope scope(ctx); + ScopedValue v(scope, pp.value); + arraySet(index, v); + } else { + arraySet(index, pp, attrs); + } return true; } @@ -1024,11 +991,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con if (attrs.isEmpty()) return true; - PropertyAttributes cattrs = Attr_Data; + PropertyAttributes cattrs; if (!member.isNull()) cattrs = internalClass->propertyData[current - memberData]; - else if (arrayData.attributes) - cattrs = arrayData.attributes[current - arrayData.data]; + else + cattrs = arrayData->attrs ? arrayData->attrs[current - arrayData->data] : Attr_Data; // clause 6 if (p.isSubset(attrs, *current, cattrs)) @@ -1084,10 +1051,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con if (!member.isNull()) { internalClass = internalClass->changeMember(member.getPointer(), cattrs); } else { - if (cattrs != Attr_Data) - ensureArrayAttributes(); - if (arrayData.attributes) - arrayData.attributes[current - arrayData.data] = cattrs; + arrayData->setAttributes(current - arrayData->data, cattrs); } if (attrs.isAccessor()) hasAccessorProperty = 1; @@ -1113,7 +1077,7 @@ void Object::copyArrayData(Object *other) Scope scope(engine()); if (other->protoHasArray() || other->hasAccessorProperty) { - uint len = other->arrayLength(); + uint len = other->getLength(); Q_ASSERT(len); ScopedValue v(scope); @@ -1121,313 +1085,97 @@ void Object::copyArrayData(Object *other) arraySet(i, (v = other->getIndexed(i))); } } else { - arrayReserve(other->arrayData.length); - arrayData.length = other->arrayData.length; - memcpy(arrayData.data, other->arrayData.data, arrayData.length*sizeof(Property)); - } - - arrayData.offset = 0; + Q_ASSERT(!arrayData && other->arrayData); + if (other->arrayType() == ArrayData::Sparse) { + SparseArrayData *od = static_cast<SparseArrayData *>(other->arrayData); + SparseArrayData *dd = new SparseArrayData; + dd->type = ArrayData::Sparse; + dd->sparse = new SparseArray(*od->sparse); + dd->freeList = od->freeList; + arrayData = dd; + } + arrayReserve(other->arrayData->len); + arrayData->len = other->arrayData->len; + // ### correctly deal with accessor properties + memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(Property)); - if (other->arrayData.sparse) { - flags &= ~SimpleArray; - arrayData.sparse = new SparseArray(*other->arrayData.sparse); - arrayData.freeList = other->arrayData.freeList; + arrayData->offset = 0; } - - setArrayLengthUnchecked(other->arrayLength()); + setArrayLengthUnchecked(other->getLength()); } - -ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +uint Object::getLength(const Managed *m) { - Q_UNUSED(ctx); - - Scope scope(engine()); - ScopedValue value(scope); - - if (!(o->flags & SimpleArray) || o->protoHasArray()) { - // lets be safe and slow - for (uint i = fromIndex; i < endIndex; ++i) { - bool exists; - value = o->getIndexed(i, &exists); - if (scope.hasException()) - return Encode::undefined(); - if (exists && __qmljs_strict_equal(value, v)) - return Encode(i); - } - } else if (arrayData.sparse) { - for (SparseArrayNode *n = arrayData.sparse->lowerBound(fromIndex); n != arrayData.sparse->end() && n->key() < endIndex; n = n->nextNode()) { - value = o->getValue(arrayData.data + n->value, arrayData.attributes ? arrayData.attributes[n->value] : Attr_Data); - if (scope.hasException()) - return Encode::undefined(); - if (__qmljs_strict_equal(value, v)) - return Encode(n->key()); - } - } else { - if (endIndex > arrayData.length) - endIndex = arrayData.length; - Property *pd = arrayData.data; - Property *end = pd + endIndex; - pd += fromIndex; - while (pd < end) { - if (!pd->value.isEmpty()) { - value = o->getValue(pd, arrayData.attributes ? arrayData.attributes[pd - arrayData.data] : Attr_Data); - if (scope.hasException()) - return Encode::undefined(); - if (__qmljs_strict_equal(value, v)) - return Encode((uint)(pd - arrayData.data)); - } - ++pd; - } - } - return Encode(-1); + Scope scope(m->engine()); + ScopedValue v(scope, const_cast<Managed *>(m)->get(scope.engine->id_length)); + return v->toUInt32(); } -void Object::arrayConcat(const ArrayObject *other) +bool Object::setArrayLength(uint newLen) { - int newLen = arrayData.length + other->arrayLength(); - if (other->arrayData.sparse) - initSparse(); - // ### copy attributes as well! - if (arrayData.sparse) { - if (other->arrayData.sparse) { - for (const SparseArrayNode *it = other->arrayData.sparse->begin(); it != other->arrayData.sparse->end(); it = it->nextNode()) - arraySet(arrayData.length + it->key(), other->arrayData.data + it->value); - } else { - int oldSize = arrayData.length; - arrayReserve(oldSize + other->arrayLength()); - memcpy(arrayData.data + oldSize, other->arrayData.data, other->arrayLength()*sizeof(Property)); - if (arrayData.attributes) - std::fill(arrayData.attributes + oldSize, arrayData.attributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); - for (uint i = 0; i < other->arrayLength(); ++i) { - SparseArrayNode *n = arrayData.sparse->insert(arrayData.length + i); - n->value = oldSize + i; - } - } + Q_ASSERT(isArrayObject()); + const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + return false; + uint oldLen = getLength(); + bool ok = true; + if (newLen < oldLen) { + uint l = arrayData->truncate(newLen); + if (l != newLen) + ok = false; + newLen = l; } else { - uint oldSize = arrayLength(); - arrayReserve(oldSize + other->arrayData.length); - if (oldSize > arrayData.length) { - for (uint i = arrayData.length; i < oldSize; ++i) - arrayData.data[i].value = Primitive::emptyValue(); - } - if (other->arrayData.attributes) { - for (uint i = 0; i < other->arrayData.length; ++i) { - bool exists; - arrayData.data[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(i, &exists); - arrayData.length = oldSize + i + 1; - if (arrayData.attributes) - arrayData.attributes[oldSize + i] = Attr_Data; - if (!exists) - arrayData.data[oldSize + i].value = Primitive::emptyValue(); - } - } else { - arrayData.length = oldSize + other->arrayData.length; - memcpy(arrayData.data + oldSize, other->arrayData.data, other->arrayData.length*sizeof(Property)); - if (arrayData.attributes) - std::fill(arrayData.attributes + oldSize, arrayData.attributes + oldSize + other->arrayData.length, PropertyAttributes(Attr_Data)); - } + if (newLen >= 0x100000) + initSparseArray(); } setArrayLengthUnchecked(newLen); + return ok; } -void Object::arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len) +void Object::initSparseArray() { - if (!arrayData.length) + if (arrayType() == ArrayData::Sparse) return; - if (arrayData.sparse) { - context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays")); + SparseArrayData *data = new SparseArrayData; + data->type = ArrayData::Sparse; + data->sparse = new SparseArray; + if (!arrayData) { + arrayData = data; return; } - if (len > arrayData.length) - len = arrayData.length; - - // The spec says the sorting goes through a series of get,put and delete operations. - // this implies that the attributes don't get sorted around. - // behavior of accessor properties is implementation defined. We simply turn them all - // into data properties and then sort. This is in line with the sentence above. - if (arrayData.attributes) { - for (uint i = 0; i < len; i++) { - if ((arrayData.attributes && arrayData.attributes[i].isGeneric()) || arrayData.data[i].value.isEmpty()) { - while (--len > i) - if (!((arrayData.attributes && arrayData.attributes[len].isGeneric())|| arrayData.data[len].value.isEmpty())) - break; - arrayData.data[i].value = getValue(arrayData.data + len, arrayData.attributes[len]); - arrayData.data[len].value = Primitive::emptyValue(); - if (arrayData.attributes) { - arrayData.attributes[i] = Attr_Data; - arrayData.attributes[len].clear(); - } - } else if (arrayData.attributes[i].isAccessor()) { - arrayData.data[i].value = getValue(arrayData.data + i, arrayData.attributes[i]); - arrayData.attributes[i] = Attr_Data; - } - } - } - - if (!(comparefn->isUndefined() || comparefn->asObject())) { - context->throwTypeError(); - return; + uint oldOffset = arrayData->offset; + data->data = arrayData->data - arrayData->offset; + data->attrs = arrayData->attrs; + data->len = arrayData->len; + data->alloc = arrayData->alloc; + data->offset = 0; + arrayData->data = 0; + arrayData->attrs = 0; + delete arrayData; + + uint *lastFree = &data->freeList; + for (uint i = 0; i < oldOffset; ++i) { + *lastFree = i; + lastFree = &data->data[i].value.uint_32; } - - ArrayElementLessThan lessThan(context, thisObject, comparefn); - - if (!len) - return; - Property *begin = arrayData.data; - std::sort(begin, begin + len, lessThan); -} - - -void Object::initSparse() -{ - if (!arrayData.sparse) { - flags &= ~SimpleArray; - arrayData.sparse = new SparseArray; - for (uint i = 0; i < arrayData.length; ++i) { - if (!((arrayData.attributes && arrayData.attributes[i].isGeneric()) || arrayData.data[i].value.isEmpty())) { - SparseArrayNode *n = arrayData.sparse->insert(i); - n->value = i + arrayData.offset; - } - } - - uint off = arrayData.offset; - if (!arrayData.offset) { - arrayData.freeList = arrayData.length; + for (uint i = 0; i < data->len; ++i) { + if (!data->data[i + oldOffset].value.isEmpty()) { + SparseArrayNode *n = data->sparse->insert(i); + n->value = i + oldOffset; } else { - arrayData.freeList = 0; - arrayData.data -= off; - arrayData.alloc += off; - int o = off; - for (int i = 0; i < o - 1; ++i) { - arrayData.data[i].value = Primitive::fromInt32(i + 1); - } - arrayData.data[o - 1].value = Primitive::fromInt32(arrayData.length + off); - } - for (uint i = arrayData.length + off; i < arrayData.alloc; ++i) { - arrayData.data[i].value = Primitive::fromInt32(i + 1); + *lastFree = i + oldOffset; + lastFree = &data->data[i + oldOffset].value.uint_32; } } -} - -void Object::arrayReserve(uint n) -{ - if (n < 8) - n = 8; - if (n >= arrayData.alloc) { - uint off; - if (arrayData.sparse) { - assert(arrayData.freeList == arrayData.alloc); - // ### FIXME - arrayData.length = arrayData.alloc; - off = 0; - } else { - off = arrayData.offset; - } - arrayData.alloc = qMax(n, 2*arrayData.alloc); - Property *newArrayData = new Property[arrayData.alloc + off]; - if (arrayData.data) { - memcpy(newArrayData + off, arrayData.data, sizeof(Property)*arrayData.length); - delete [] (arrayData.data - off); - } - arrayData.data = newArrayData + off; - if (arrayData.sparse) { - for (uint i = arrayData.freeList; i < arrayData.alloc; ++i) { - arrayData.data[i].value = Primitive::emptyValue(); - arrayData.data[i].value = Primitive::fromInt32(i + 1); - } - } - - if (arrayData.attributes) { - PropertyAttributes *newAttrs = new PropertyAttributes[arrayData.alloc]; - memcpy(newAttrs, arrayData.attributes, sizeof(PropertyAttributes)*arrayData.length); - delete [] (arrayData.attributes - off); - - arrayData.attributes = newAttrs; - if (arrayData.sparse) { - for (uint i = arrayData.freeList; i < arrayData.alloc; ++i) - arrayData.attributes[i] = Attr_Invalid; - } - } + for (uint i = data->len + oldOffset; i < data->alloc; ++i) { + *lastFree = i; + lastFree = &data->data[i].value.uint_32; } + arrayData = data; } -void Object::ensureArrayAttributes() -{ - if (arrayData.attributes) - return; - - flags &= ~SimpleArray; - uint off = arrayData.sparse ? 0 : arrayData.offset; - arrayData.attributes = new PropertyAttributes[arrayData.alloc + off]; - arrayData.attributes += off; - for (uint i = 0; i < arrayData.length; ++i) - arrayData.attributes[i] = Attr_Data; - for (uint i = arrayData.length; i < arrayData.alloc; ++i) - arrayData.attributes[i] = Attr_Invalid; -} - - -bool Object::setArrayLength(uint newLen) { - assert(isArrayObject()); - const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; - if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) - return false; - uint oldLen = arrayLength(); - bool ok = true; - if (newLen < oldLen) { - if (arrayData.sparse) { - SparseArrayNode *begin = arrayData.sparse->lowerBound(newLen); - if (begin != arrayData.sparse->end()) { - SparseArrayNode *it = arrayData.sparse->end()->previousNode(); - while (1) { - Property &pd = arrayData.data[it->value]; - if (arrayData.attributes) { - if (!arrayData.attributes[it->value].isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; - } else { - arrayData.attributes[it->value].clear(); - } - } - pd.value.tag = Value::Empty_Type; - pd.value.int_32 = arrayData.freeList; - arrayData.freeList = it->value; - bool brk = (it == begin); - SparseArrayNode *prev = it->previousNode(); - arrayData.sparse->erase(it); - if (brk) - break; - it = prev; - } - } - } else { - Property *it = arrayData.data + arrayData.length; - const Property *begin = arrayData.data + newLen; - while (--it >= begin) { - if (arrayData.attributes) { - if (!arrayData.attributes[it - arrayData.data].isEmpty() && !arrayData.attributes[it - arrayData.data].isConfigurable()) { - ok = false; - newLen = it - arrayData.data + 1; - break; - } else { - arrayData.attributes[it - arrayData.data].clear(); - } - it->value = Primitive::emptyValue(); - } - } - arrayData.length = newLen; - } - } else { - if (newLen >= 0x100000) - initSparse(); - } - setArrayLengthUnchecked(newLen); - return ok; -} DEFINE_MANAGED_VTABLE(ArrayObject); @@ -1444,9 +1192,10 @@ ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) // elements converted to JS Strings. int len = list.count(); arrayReserve(len); + ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { - arrayData.data[ii].value = Encode(engine->newString(list.at(ii))); - arrayData.length = ii + 1; + arrayData->put(ii, (v = engine->newString(list.at(ii)))); + arrayData->setLength(ii + 1); } setArrayLengthUnchecked(len); } @@ -1458,6 +1207,25 @@ void ArrayObject::init(ExecutionEngine *engine) memberData[LengthPropertyIndex].value = Primitive::fromInt32(0); } +ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l) +{ + if (l->name->equals(m->engine()->id_length)) { + // special case, as the property is on the object itself + l->getter = Lookup::arrayLengthGetter; + ArrayObject *a = static_cast<ArrayObject *>(m); + return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue(); + } + return Object::getLookup(m, l); +} + +uint ArrayObject::getLength(const Managed *m) +{ + const ArrayObject *a = static_cast<const ArrayObject *>(m); + if (a->memberData[ArrayObject::LengthPropertyIndex].value.isInteger()) + return a->memberData[ArrayObject::LengthPropertyIndex].value.integerValue(); + return Primitive::toUInt32(a->memberData[ArrayObject::LengthPropertyIndex].value.doubleValue()); +} + QStringList ArrayObject::toQStringList() const { QStringList result; @@ -1466,7 +1234,7 @@ QStringList ArrayObject::toQStringList() const Scope scope(engine); ScopedValue v(scope); - uint32_t length = arrayLength(); + uint32_t length = getLength(); for (uint32_t i = 0; i < length; ++i) { v = const_cast<ArrayObject *>(this)->getIndexed(i); result.append(v->toQStringNoThrow()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index a5f118c359..b9fc05eb64 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -49,7 +49,7 @@ #include "qv4managed_p.h" #include "qv4property_p.h" #include "qv4internalclass_p.h" -#include "qv4sparsearray_p.h" +#include "qv4arraydata_p.h" #include <QtCore/QString> #include <QtCore/QHash> @@ -100,6 +100,7 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; + struct Q_QML_EXPORT Object: Managed { Q_MANAGED Q_MANAGED_TYPE(Object) @@ -109,18 +110,7 @@ struct Q_QML_EXPORT Object: Managed { uint memberDataAlloc; Property *memberData; - struct ArrayData { - union { - uint freeList; - uint offset; - }; - uint length; - uint alloc; - PropertyAttributes *attributes; - Property *data; - SparseArray *sparse; - }; - ArrayData arrayData; + ArrayData *arrayData; enum { InlinePropertySize = 4 @@ -183,107 +173,54 @@ struct Q_QML_EXPORT Object: Managed { // Array handling - uint allocArrayValue() { - uint idx = arrayData.freeList; - if (arrayData.alloc <= arrayData.freeList) - arrayReserve(arrayData.alloc + 1); - arrayData.freeList = arrayData.data[arrayData.freeList].value.uint_32; - if (arrayData.attributes) - arrayData.attributes[idx].setType(PropertyAttributes::Data); - return idx; - } - - uint allocArrayValue(const ValueRef v) { - uint idx = allocArrayValue(); - Property *pd = &arrayData.data[idx]; - pd->value = *v; - return idx; - } - void freeArrayValue(int idx) { - Property &pd = arrayData.data[idx]; - pd.value.tag = Value::Empty_Type; - pd.value.int_32 = arrayData.freeList; - arrayData.freeList = idx; - if (arrayData.attributes) - arrayData.attributes[idx].clear(); - } - - void getArrayHeadRoom() { - assert(!arrayData.sparse && !arrayData.offset); - arrayData.offset = qMax(arrayData.length >> 2, (uint)16); - Property *newArray = new Property[arrayData.offset + arrayData.alloc]; - memcpy(newArray + arrayData.offset, arrayData.data, arrayData.length*sizeof(Property)); - delete [] arrayData.data; - arrayData.data = newArray + arrayData.offset; - if (arrayData.attributes) { - PropertyAttributes *newAttrs = new PropertyAttributes[arrayData.offset + arrayData.alloc]; - memcpy(newAttrs + arrayData.offset, arrayData.attributes, arrayData.length*sizeof(PropertyAttributes)); - delete [] arrayData.attributes; - arrayData.attributes = newAttrs + arrayData.offset; - } - } - public: void copyArrayData(Object *other); - void initSparse(); - uint arrayLength() const; bool setArrayLength(uint newLen); void setArrayLengthUnchecked(uint l); - Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data); + void arraySet(uint index, const Property &p, PropertyAttributes attributes = Attr_Data); - void arraySet(uint index, const Property *pd); void arraySet(uint index, ValueRef value); - uint propertyIndexFromArrayIndex(uint index) const - { - if (!arrayData.sparse) { - if (index >= arrayData.length) - return UINT_MAX; - return index; - } else { - SparseArrayNode *n = arrayData.sparse->findNode(index); - if (!n) - return UINT_MAX; - return n->value; - } - } + void push_back(const ValueRef v); - Property *arrayAt(uint index) const { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx == UINT_MAX) - return 0; - return arrayData.data + pidx; + ArrayData::Type arrayType() const { + return arrayData ? (ArrayData::Type)arrayData->type : ArrayData::Simple; } - - Property *nonSparseArrayAt(uint index) const { - if (arrayData.sparse) - return 0; - if (index >= arrayData.length) - return 0; - return arrayData.data + index; + // ### remove me + void setArrayType(ArrayData::Type t) { + Q_ASSERT(t != ArrayData::Simple && t != ArrayData::Sparse); + arrayCreate(); + arrayData->type = t; } - void push_back(const ValueRef v); + inline void arrayReserve(uint n) { + arrayCreate(); + arrayData->vtable->reserve(arrayData, n); + } - SparseArrayNode *sparseArrayBegin() { return arrayData.sparse ? arrayData.sparse->begin() : 0; } - SparseArrayNode *sparseArrayEnd() { return arrayData.sparse ? arrayData.sparse->end() : 0; } + void arrayCreate() { + if (!arrayData) + arrayData = new ArrayData; + } - void arrayConcat(const ArrayObject *other); - void arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint dataLen); - ReturnedValue arrayIndexOf(const ValueRef v, uint fromIndex, uint dataLen, ExecutionContext *ctx, Object *o); + void initSparseArray(); + SparseArrayNode *sparseBegin() { return arrayType() == ArrayData::Sparse ? static_cast<SparseArrayData *>(arrayData)->sparse->begin() : 0; } + SparseArrayNode *sparseEnd() { return arrayType() == ArrayData::Sparse ? static_cast<SparseArrayData *>(arrayData)->sparse->end() : 0; } - void arrayReserve(uint n); - void ensureArrayAttributes(); + inline Property *arrayInsert(uint index) { + arrayCreate(); + return ArrayData::insert(this, index); + } inline bool protoHasArray() { Scope scope(engine()); Scoped<Object> p(scope, this); while ((p = p->prototype())) - if (p->arrayData.length) + if (p->arrayData) return true; return false; @@ -309,6 +246,7 @@ public: using Managed::getLookup; using Managed::setLookup; using Managed::advanceIterator; + using Managed::getLength; protected: static void destroy(Managed *that); static void markObjects(Managed *that, ExecutionEngine *e); @@ -323,7 +261,7 @@ protected: static ReturnedValue getLookup(Managed *m, Lookup *l); static void setLookup(Managed *m, Lookup *l, const ValueRef v); static Property *advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); - + static uint getLength(const Managed *m); private: ReturnedValue internalGet(const StringRef name, bool *hasProperty); @@ -382,19 +320,13 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); + static ReturnedValue getLookup(Managed *m, Lookup *l); + using Managed::getLength; + static uint getLength(const Managed *m); + QStringList toQStringList() const; }; -inline uint Object::arrayLength() const -{ - if (isArrayObject()) { - if (memberData[ArrayObject::LengthPropertyIndex].value.isInteger()) - return memberData[ArrayObject::LengthPropertyIndex].value.integerValue(); - return Primitive::toUInt32(memberData[ArrayObject::LengthPropertyIndex].value.doubleValue()); - } - return 0; -} - inline void Object::setArrayLengthUnchecked(uint l) { if (isArrayObject()) { @@ -406,64 +338,35 @@ inline void Object::setArrayLengthUnchecked(uint l) inline void Object::push_back(const ValueRef v) { - uint idx = arrayLength(); - if (!arrayData.sparse) { - if (idx >= arrayData.alloc) - arrayReserve(idx + 1); - arrayData.data[idx].value = *v; - arrayData.length = idx + 1; - } else { - uint idx = allocArrayValue(v); - arrayData.sparse->push_back(idx, arrayLength()); - } + arrayCreate(); + Q_ASSERT(!arrayData->isSparse()); + + uint idx = getLength(); + arrayReserve(idx + 1); + arrayData->put(idx, v); + arrayData->setLength(idx + 1); setArrayLengthUnchecked(idx + 1); } -inline Property *Object::arrayInsert(uint index, PropertyAttributes attributes) { +inline void Object::arraySet(uint index, const Property &p, PropertyAttributes attributes) +{ if (attributes.isAccessor()) hasAccessorProperty = 1; - Property *pd; - if (!arrayData.sparse && (index < 0x1000 || index < arrayData.length + (arrayData.length >> 2))) { - if (index >= arrayData.alloc) - arrayReserve(index + 1); - if (index >= arrayData.length) { - // mark possible hole in the array - for (uint i = arrayData.length; i < index; ++i) { - arrayData.data[i].value = Primitive::emptyValue(); - if (arrayData.attributes) - arrayData.attributes[i].clear(); - } - arrayData.length = index + 1; - } - pd = arrayData.data + index; - } else { - initSparse(); - SparseArrayNode *n = arrayData.sparse->insert(index); - if (n->value == UINT_MAX) - n->value = allocArrayValue(); - pd = arrayData.data + n->value; - } - if (index >= arrayLength()) + Property *pd = arrayInsert(index); + *pd = p; + arrayData->setAttributes(index, attributes); + if (isArrayObject() && index >= getLength()) setArrayLengthUnchecked(index + 1); - if (arrayData.attributes || attributes != Attr_Data) { - if (!arrayData.attributes) - ensureArrayAttributes(); - attributes.resolve(); - arrayData.attributes[pd - arrayData.data] = attributes; - } - return pd; } + inline void Object::arraySet(uint index, ValueRef value) { Property *pd = arrayInsert(index); - pd->value = *value; -} - -inline void Object::arraySet(uint index, const Property *pd) -{ - *arrayInsert(index) = *pd; + pd->value = value ? *value : Primitive::undefinedValue(); + if (isArrayObject() && index >= getLength()) + setArrayLengthUnchecked(index + 1); } template<> diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 6bce8a965c..f319b99a1a 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -271,10 +271,12 @@ ReturnedValue ObjectPrototype::method_seal(CallContext *ctx) o->internalClass = o->internalClass->sealed(); - o->ensureArrayAttributes(); - for (uint i = 0; i < o->arrayData.length; ++i) { - if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty())) - o->arrayData.attributes[i].setConfigurable(false); + if (o->arrayData) { + o->arrayData->ensureAttributes(); + for (uint i = 0; i < o->arrayData->length(); ++i) { + if (!o->arrayData->isEmpty(i)) + o->arrayData->attrs[i].setConfigurable(false); + } } return o.asReturnedValue(); @@ -294,12 +296,14 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx) o->internalClass = o->internalClass->frozen(); - o->ensureArrayAttributes(); - for (uint i = 0; i < o->arrayData.length; ++i) { - if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty())) - o->arrayData.attributes[i].setConfigurable(false); - if (o->arrayData.attributes[i].isData()) - o->arrayData.attributes[i].setWritable(false); + if (o->arrayData) { + o->arrayData->ensureAttributes(); + for (uint i = 0; i < o->arrayData->length(); ++i) { + if (!o->arrayData->isEmpty(i)) + o->arrayData->attrs[i].setConfigurable(false); + if (o->arrayData->attrs[i].isData()) + o->arrayData->attrs[i].setWritable(false); + } } return o.asReturnedValue(); } @@ -328,15 +332,16 @@ ReturnedValue ObjectPrototype::method_isSealed(CallContext *ctx) if (o->internalClass != o->internalClass->sealed()) return Encode(false); - if (!o->arrayData.length) + if (!o->arrayData || !o->arrayData->length()) return Encode(true); - if (!o->arrayData.attributes) + if (o->arrayData->length() && !o->arrayData->attrs) return Encode(false); - for (uint i = 0; i < o->arrayData.length; ++i) { - if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty())) - if (o->arrayData.attributes[i].isConfigurable()) + for (uint i = 0; i < o->arrayData->length(); ++i) { + // ### Fix for sparse arrays + if (!o->arrayData->isEmpty(i)) + if (o->arrayData->attributes(i).isConfigurable()) return Encode(false); } @@ -356,15 +361,16 @@ ReturnedValue ObjectPrototype::method_isFrozen(CallContext *ctx) if (o->internalClass != o->internalClass->frozen()) return Encode(false); - if (!o->arrayData.length) + if (!o->arrayData->length()) return Encode(true); - if (!o->arrayData.attributes) + if (o->arrayData->length() && !o->arrayData->attrs) return Encode(false); - for (uint i = 0; i < o->arrayData.length; ++i) { - if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty())) - if (o->arrayData.attributes[i].isConfigurable() || o->arrayData.attributes[i].isWritable()) + for (uint i = 0; i < o->arrayData->length(); ++i) { + // ### Fix for sparse arrays + if (!o->arrayData->isEmpty(i)) + if (o->arrayData->attributes(i).isConfigurable() || o->arrayData->attributes(i).isWritable()) return Encode(false); } diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 9763de5adf..ff9ea59514 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -53,7 +53,7 @@ struct FunctionObject; struct Property { union { - Value value; + SafeValue value; struct { FunctionObject *get; FunctionObject *set; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 4293cca327..bad2cf3cd4 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1592,7 +1592,7 @@ void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::ValueRe if (array) { Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); - uint32_t length = array->arrayLength(); + uint32_t length = array->getLength(); for (uint32_t ii = 0; ii < length; ++ii) { QObject *o = 0; qobjectWrapper = array->getIndexed(ii); @@ -1689,9 +1689,10 @@ QV4::ReturnedValue CallArgument::toValue(QV8Engine *engine) QList<QObject *> &list = *qlistPtr; QV4::Scoped<ArrayObject> array(scope, v4->newArrayObject()); array->arrayReserve(list.count()); + QV4::ScopedValue v(scope); for (int ii = 0; ii < list.count(); ++ii) { - array->arrayData.data[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii)); - array->arrayData.length = ii + 1; + array->arrayData->put(ii, (v = QV4::QObjectWrapper::wrap(v4, list.at(ii)))); + array->arrayData->setLength(ii + 1); } array->setArrayLengthUnchecked(list.count()); return array.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index e8d92efb58..18132505bc 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -344,11 +344,13 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) Scoped<ArrayObject> array(scope, ctx->engine->newArrayObject(ctx->engine->regExpExecArrayClass)); int len = r->value->captureCount(); array->arrayReserve(len); + ScopedValue v(scope); for (int i = 0; i < len; ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; - array->arrayData.data[i].value = (start != -1 && end != -1) ? ctx->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); - array->arrayData.length = i + 1; + v = (start != -1 && end != -1) ? ctx->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); + array->arrayData->put(i, v); + array->arrayData->setLength(i + 1); } array->setArrayLengthUnchecked(len); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 23549fe7ef..91ddfd01de 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -589,12 +589,10 @@ ReturnedValue __qmljs_get_element(ExecutionContext *ctx, const ValueRef object, } if (idx < UINT_MAX) { - uint pidx = o->propertyIndexFromArrayIndex(idx); - if (pidx < UINT_MAX) { - if (!o->arrayData.attributes || o->arrayData.attributes[pidx].isData()) { - if (!o->arrayData.data[pidx].value.isEmpty()) - return o->arrayData.data[pidx].value.asReturnedValue(); - } + if (!o->arrayData->hasAttributes()) { + ScopedValue v(scope, o->arrayData->get(idx)); + if (!v->isEmpty()) + return v->asReturnedValue(); } return o->getIndexed(idx); @@ -615,36 +613,10 @@ void __qmljs_set_element(ExecutionContext *ctx, const ValueRef object, const Val uint idx = index->asArrayIndex(); if (idx < UINT_MAX) { - uint pidx = o->propertyIndexFromArrayIndex(idx); - if (pidx < UINT_MAX && !o->asArgumentsObject()) { - if (o->arrayData.attributes && !o->arrayData.attributes[pidx].isEmpty() && !o->arrayData.attributes[pidx].isWritable()) { - if (ctx->strictMode) - ctx->throwTypeError(); - return; - } - - Property *p = o->arrayData.data + pidx; - if (!o->arrayData.attributes || o->arrayData.attributes[pidx].isData()) { - p->value = *value; - return; - } - - if (o->arrayData.attributes[pidx].isAccessor()) { - FunctionObject *setter = p->setter(); - if (!setter) { - if (ctx->strictMode) - ctx->throwTypeError(); - return; - } - - ScopedCallData callData(scope, 1); - callData->thisObject = o; - callData->args[0] = *value; - setter->call(callData); - return; - } - } - o->putIndexed(idx, value); + if (idx < o->arrayData->length() && o->arrayType() == ArrayData::Simple) + o->arrayData->put(idx, value); + else + o->putIndexed(idx, value); return; } @@ -1118,25 +1090,23 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const ValueRef objec assert(o); uint idx = name->asArrayIndex(); - Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); - pd->value = val ? *val : Primitive::undefinedValue(); + if (idx != UINT_MAX) { + o->arraySet(idx, val); + } else { + Property *pd = o->insertMember(name, Attr_Data); + pd->value = val ? *val : Primitive::undefinedValue(); + } } -ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, Value *values, uint length) +ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, SafeValue *values, uint length) { Scope scope(ctx); Scoped<ArrayObject> a(scope, ctx->engine->newArrayObject()); - // ### FIXME: We need to allocate the array data to avoid crashes other places - // This should rather be done when required - a->arrayReserve(length); if (length) { - a->arrayData.length = length; - Property *pd = a->arrayData.data; - for (uint i = 0; i < length; ++i) { - pd->value = values[i]; - ++pd; - } + a->arrayReserve(length); + a->arrayData->setLength(length); + a->arrayData->put(0, values, length); a->setArrayLengthUnchecked(length); } return a.asReturnedValue(); @@ -1149,9 +1119,16 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const ValueRef Q_ASSERT(!!o); uint idx = name->asArrayIndex(); - Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); - pd->setGetter(getter ? getter->asFunctionObject() : 0); - pd->setSetter(setter ? setter->asFunctionObject() : 0); + if (idx != UINT_MAX) { + Property pd; + pd.setGetter(getter ? getter->asFunctionObject() : 0); + pd.setSetter(setter ? setter->asFunctionObject() : 0); + o->arraySet(idx, pd, Attr_Accessor); + } else { + Property *pd = o->insertMember(name, Attr_Accessor); + pd->setGetter(getter ? getter->asFunctionObject() : 0); + pd->setSetter(setter ? setter->asFunctionObject() : 0); + } } ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, const QV4::Value *args, int classId) diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 9524b2459c..2e252b1067 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -141,7 +141,7 @@ QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx); ReturnedValue __qmljs_builtin_unwind_exception(ExecutionContext *ctx); void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, const QV4::StringRef name); void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, QV4::ValueRef val); -QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *values, uint length); +QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, SafeValue *values, uint length); void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, const QV4::ValueRef getter, const QV4::ValueRef setter); QV4::ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, const QV4::Value *args, int classId); QV4::ReturnedValue __qmljs_builtin_setup_arguments_object(ExecutionContext *ctx); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 988762c842..feb3806ba5 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -175,7 +175,7 @@ public: , m_propertyIndex(-1) , m_isReference(false) { - flags &= ~SimpleArray; + setArrayType(ArrayData::Custom); QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); Q_UNUSED(protectThis); @@ -188,7 +188,7 @@ public: , m_propertyIndex(propertyIndex) , m_isReference(true) { - flags &= ~SimpleArray; + setArrayType(ArrayData::Custom); QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); Q_UNUSED(protectThis); @@ -468,7 +468,7 @@ public: { QV4::Scope scope(array->engine()); Container result; - quint32 length = array->arrayLength(); + quint32 length = array->getLength(); QV4::ScopedValue v(scope); for (quint32 i = 0; i < length; ++i) result << convertValueToElement<typename Container::value_type>((v = array->getIndexed(i))); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index b199c86d78..5f9e24d89c 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -183,7 +183,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en push(data, valueheader(WorkerUndefined)); } else if (v->asArrayObject()) { QV4::ScopedArrayObject array(scope, v); - uint32_t length = array->arrayLength(); + uint32_t length = array->getLength(); if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); return; @@ -266,7 +266,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en // regular object QV4::ScopedValue val(scope, *v); QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(v4, val)); - quint32 length = properties->arrayLength(); + quint32 length = properties->getLength(); if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); return; @@ -390,8 +390,8 @@ ReturnedValue Serialize::deserialize(const char *&data, QV8Engine *engine) array->arrayReserve(seqLength); for (quint32 ii = 0; ii < seqLength; ++ii) { value = deserialize(data, engine); - array->arrayData.data[ii].value = value.asReturnedValue(); - array->arrayData.length = ii + 1; + array->arrayData->put(ii, value); + array->arrayData->setLength(ii + 1); } array->setArrayLengthUnchecked(seqLength); QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index b97b23bf44..df48faf601 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -126,6 +126,7 @@ const ManagedVTable String::static_vtbl = 0 /*getLookup*/, 0 /*setLookup*/, isEqualTo, + 0, 0 /*advanceIterator*/, "String", }; @@ -422,3 +423,8 @@ uint String::toArrayIndex(const QString &str) bool ok; return ::toArrayIndex(str.constData(), str.constData() + str.length(), &ok); } + +uint String::getLength(const Managed *m) +{ + return static_cast<const String *>(m)->length(); +} diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 7e2824d8a0..bc5b30518e 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -91,6 +91,7 @@ struct Q_QML_EXPORT String : public Managed { return toQString() == other->toQString(); } + inline bool compare(const String *other) { return toQString() < other->toQString(); } @@ -172,6 +173,7 @@ protected: static bool deleteProperty(Managed *, const StringRef); static bool deleteIndexedProperty(Managed *m, uint index); static bool isEqualTo(Managed *that, Managed *o); + static uint getLength(const Managed *m); private: QChar *recursiveAppend(QChar *ch) const; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 3d90c812c4..1781ce2581 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -144,14 +144,20 @@ Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, StringRe while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; - if (attrs) - *attrs = s->arrayData.attributes ? s->arrayData.attributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); - return s->__getOwnProperty__(*index); + PropertyAttributes a; + Property *p = s->__getOwnProperty__(*index, &a); + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + if (attrs) + *attrs = a; + return p; + } + } + if (s->arrayData) { + it->arrayNode = s->sparseBegin(); + // iterate until we're past the end of the string + while (it->arrayNode && it->arrayNode->key() < slen) + it->arrayNode = it->arrayNode->nextNode(); } - it->arrayNode = s->sparseArrayBegin(); - // iterate until we're past the end of the string - while (it->arrayNode && it->arrayNode->key() < slen) - it->arrayNode = it->arrayNode->nextNode(); } return Object::advanceIterator(m, it, name, index, attrs); @@ -692,18 +698,18 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) array->push_back((s = ctx->engine->newString(text.mid(offset, matchOffsets[0] - offset)))); offset = qMax(offset + 1, matchOffsets[1]); - if (array->arrayLength() >= limit) + if (array->getLength() >= limit) break; for (int i = 1; i < re->value->captureCount(); ++i) { uint start = matchOffsets[i * 2]; uint end = matchOffsets[i * 2 + 1]; array->push_back((s = ctx->engine->newString(text.mid(start, end - start)))); - if (array->arrayLength() >= limit) + if (array->getLength() >= limit) break; } } - if (array->arrayLength() < limit) + if (array->getLength() < limit) array->push_back((s = ctx->engine->newString(text.mid(offset)))); } else { QString separator = separatorValue->toString(ctx)->toQString(); @@ -718,10 +724,10 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx) while ((end = text.indexOf(separator, start)) != -1) { array->push_back((s = ctx->engine->newString(text.mid(start, end - start)))); start = end + separator.size(); - if (array->arrayLength() >= limit) + if (array->getLength() >= limit) break; } - if (array->arrayLength() < limit && start != -1) + if (array->getLength() < limit && start != -1) array->push_back((s = ctx->engine->newString(text.mid(start)))); } return array.asReturnedValue(); diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 7b975c2cc8..76c11cb748 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -57,7 +57,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine) v8(engine) { setVTable(&static_vtbl); - flags &= ~SimpleArray; + setArrayType(ArrayData::Custom); } QmlListWrapper::~QmlListWrapper() diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 94cf268858..75a366a7af 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -554,12 +554,12 @@ QV4::ReturnedValue QQmlLocaleData::method_get_weekDays(QV4::CallContext *ctx) QV4::Scoped<QV4::ArrayObject> result(scope, ctx->engine->newArrayObject()); result->arrayReserve(days.size()); - result->arrayData.length = days.size(); + result->arrayData->setLength(days.size()); for (int i = 0; i < days.size(); ++i) { int day = days.at(i); if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday) day = 0; - result->arrayData.data[i].value = QV4::Primitive::fromInt32(day); + result->arrayData->put(i, QV4::Primitive::fromInt32(day)); } result->setArrayLengthUnchecked(days.size()); @@ -576,9 +576,10 @@ QV4::ReturnedValue QQmlLocaleData::method_get_uiLanguages(QV4::CallContext *ctx) QStringList langs = locale->uiLanguages(); QV4::Scoped<QV4::ArrayObject> result(scope, ctx->engine->newArrayObject()); result->arrayReserve(langs.size()); + QV4::ScopedValue v(scope); for (int i = 0; i < langs.size(); ++i) { - result->arrayData.data[i].value = ctx->engine->newString(langs.at(i)); - result->arrayData.length = i + 1; + result->arrayData->put(i, (v = ctx->engine->newString(langs.at(i)))); + result->arrayData->setLength(i + 1); } result->setArrayLengthUnchecked(langs.size()); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 1e7fdbc316..c5c2a69605 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -164,7 +164,7 @@ QVariant QV8Engine::toVariant(const QV4::ValueRef value, int typeHint) QV4::ScopedArrayObject a(scope, value); if (typeHint == qMetaTypeId<QList<QObject *> >()) { QList<QObject *> list; - uint32_t length = a->arrayLength(); + uint32_t length = a->getLength(); QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); for (uint32_t ii = 0; ii < length; ++ii) { qobjectWrapper = a->getIndexed(ii); @@ -196,9 +196,10 @@ static QV4::ReturnedValue arrayFromStringList(QV8Engine *engine, const QStringLi QV4::Scoped<QV4::ArrayObject> a(scope, e->newArrayObject()); int len = list.count(); a->arrayReserve(len); + QV4::ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { - a->arrayData.data[ii].value = QV4::Encode(e->newString(list.at(ii))); - a->arrayData.length = ii + 1; + a->arrayData->put(ii, (v = QV4::Encode(e->newString(list.at(ii))))); + a->arrayData->setLength(ii + 1); } a->setArrayLengthUnchecked(len); return a.asReturnedValue(); @@ -211,9 +212,10 @@ static QV4::ReturnedValue arrayFromVariantList(QV8Engine *engine, const QVariant QV4::Scoped<QV4::ArrayObject> a(scope, e->newArrayObject()); int len = list.count(); a->arrayReserve(len); + QV4::ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { - a->arrayData.data[ii].value = engine->fromVariant(list.at(ii)); - a->arrayData.length = ii + 1; + a->arrayData->put(ii, (v = engine->fromVariant(list.at(ii)))); + a->arrayData->setLength(ii + 1); } a->setArrayLengthUnchecked(len); return a.asReturnedValue(); @@ -325,9 +327,10 @@ QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant) const QList<QObject *> &list = *(QList<QObject *>*)ptr; QV4::Scoped<QV4::ArrayObject> a(scope, m_v4Engine->newArrayObject()); a->arrayReserve(list.count()); + QV4::ScopedValue v(scope); for (int ii = 0; ii < list.count(); ++ii) { - a->arrayData.data[ii].value = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii)); - a->arrayData.length = ii + 1; + a->arrayData->put(ii, (v = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii)))); + a->arrayData->setLength(ii + 1); } a->setArrayLengthUnchecked(list.count()); return a.asReturnedValue(); @@ -407,7 +410,7 @@ QVariant QV8Engine::toBasicVariant(const QV4::ValueRef value) QV4::ScopedValue v(scope); QVariantList rv; - int length = a->arrayLength(); + int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { v = a->getIndexed(ii); rv << toVariant(v, -1); @@ -540,9 +543,10 @@ QV4::ReturnedValue QV8Engine::variantListToJS(const QVariantList &lst) QV4::Scope scope(m_v4Engine); QV4::Scoped<QV4::ArrayObject> a(scope, m_v4Engine->newArrayObject()); a->arrayReserve(lst.size()); + QV4::ScopedValue v(scope); for (int i = 0; i < lst.size(); i++) { - a->arrayData.data[i].value = variantToJS(lst.at(i)); - a->arrayData.length = i + 1; + a->arrayData->put(i, (v = variantToJS(lst.at(i)))); + a->arrayData->setLength(i + 1); } a->setArrayLengthUnchecked(lst.size()); return a.asReturnedValue(); @@ -568,7 +572,7 @@ QVariantList QV8Engine::variantListFromJS(QV4::ArrayObjectRef a, QV4::Scope scope(a->engine()); QV4::ScopedValue v(scope); - quint32 length = a->arrayLength(); + quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); result.append(variantFromJS(v, visitedObjects)); diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 32ebb93279..40150d62ba 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1692,7 +1692,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::ValueRef groups) const QV4::ScopedArrayObject array(scope, groups); if (array) { QV4::ScopedValue v(scope); - uint arrayLength = array->arrayLength(); + uint arrayLength = array->getLength(); for (uint i = 0; i < arrayLength; ++i) { v = array->getIndexed(i); const QString groupName = v->toQString(); @@ -3189,7 +3189,7 @@ public: : Object(engine) { setVTable(&static_vtbl); - flags &= ~SimpleArray; + setArrayType(QV4::ArrayData::Custom); } virtual ~QQmlDelegateModelGroupChangeArray() {} diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 7e441023c9..eeb4aa0861 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -441,7 +441,7 @@ void ListModel::set(int elementIndex, QV4::ObjectRef object, QVector<int> *roles const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); ListModel *subModel = new ListModel(r.subLayout, 0, -1); - int arrayLength = a->arrayLength(); + int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); subModel->append(o, eng); @@ -517,7 +517,7 @@ void ListModel::set(int elementIndex, QV4::ObjectRef object, QV8Engine *eng) if (r.type == ListLayout::Role::List) { ListModel *subModel = new ListModel(r.subLayout, 0, -1); - int arrayLength = a->arrayLength(); + int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); subModel->append(o, eng); @@ -1188,7 +1188,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::ValueRef QV4::Scoped<QV4::Object> o(scope); ListModel *subModel = new ListModel(role.subLayout, 0, -1); - int arrayLength = a->arrayLength(); + int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); subModel->append(o, eng); @@ -1953,7 +1953,7 @@ void QQmlListModel::insert(QQmlV4Function *args) if (objectArray) { QV4::ScopedObject argObject(scope); - int objectArrayLength = objectArray->arrayLength(); + int objectArrayLength = objectArray->getLength(); for (int i=0 ; i < objectArrayLength ; ++i) { argObject = objectArray->getIndexed(i); @@ -2055,7 +2055,7 @@ void QQmlListModel::append(QQmlV4Function *args) if (objectArray) { QV4::Scoped<QV4::Object> argObject(scope); - int objectArrayLength = objectArray->arrayLength(); + int objectArrayLength = objectArray->getLength(); int index = count(); for (int i=0 ; i < objectArrayLength ; ++i) { diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b6d4f1073c..c8741335dd 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -871,7 +871,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object : QV4::Object(engine) { setVTable(&static_vtbl); - flags &= ~SimpleArray; + setArrayType(QV4::ArrayData::Custom); } static void destroy(QV4::Managed *that) { diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp index 152d1f34c9..5ae74c2fec 100644 --- a/src/quick/util/qquickglobal.cpp +++ b/src/quick/util/qquickglobal.cpp @@ -398,7 +398,7 @@ public: if (!array) return QMatrix4x4(); - if (array->arrayLength() != 16) + if (array->getLength() != 16) return QMatrix4x4(); float matVals[16]; diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index ea7259c597..b11ad2dd0e 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -3910,7 +3910,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { QV4::Scope scope(v4); QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts); QV4::ScopedValue qml(scope); - for (quint32 i = 0; i < scripts->arrayLength(); ++i) { + for (quint32 i = 0; i < scripts->getLength(); ++i) { QQmlContextData *scriptContext, *newContext; qml = scripts->getIndexed(i); |