aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/imports/localstorage/plugin.cpp2
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp11
-rw-r--r--src/qml/jsruntime/jsruntime.pri2
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp34
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp563
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h242
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp195
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4context.cpp1
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp13
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp9
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h1
-rw-r--r--src/qml/jsruntime/qv4managed.cpp1
-rw-r--r--src/qml/jsruntime/qv4managed_p.h12
-rw-r--r--src/qml/jsruntime/qv4object.cpp556
-rw-r--r--src/qml/jsruntime/qv4object_p.h199
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp46
-rw-r--r--src/qml/jsruntime/qv4property_p.h2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp7
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp79
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h2
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4serialize.cpp8
-rw-r--r--src/qml/jsruntime/qv4string.cpp6
-rw-r--r--src/qml/jsruntime/qv4string_p.h2
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp30
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp2
-rw-r--r--src/qml/qml/qqmllocale.cpp9
-rw-r--r--src/qml/qml/v8/qv8engine.cpp26
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp4
-rw-r--r--src/qml/types/qqmllistmodel.cpp10
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp2
-rw-r--r--src/quick/util/qquickglobal.cpp2
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp2
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);