Rework array handling for JS objects
authorLars Knoll <lars.knoll@digia.com>
Mon, 16 Dec 2013 08:16:57 +0000 (09:16 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 9 Jan 2014 06:47:06 +0000 (07:47 +0100)
Split up ArrayData into two classes, one for regular
arrays, one for sparse arrays and cleanly separate
the two cases. Only create array data on demand.

Change-Id: I9ca8d0b53592174f213ba0f20caf93e77dba690a
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
36 files changed:
src/imports/localstorage/plugin.cpp
src/qml/compiler/qv4isel_masm.cpp
src/qml/jsruntime/jsruntime.pri
src/qml/jsruntime/qv4argumentsobject.cpp
src/qml/jsruntime/qv4arraydata.cpp [new file with mode: 0644]
src/qml/jsruntime/qv4arraydata_p.h [new file with mode: 0644]
src/qml/jsruntime/qv4arrayobject.cpp
src/qml/jsruntime/qv4arrayobject_p.h
src/qml/jsruntime/qv4context.cpp
src/qml/jsruntime/qv4functionobject.cpp
src/qml/jsruntime/qv4jsonobject.cpp
src/qml/jsruntime/qv4lookup.cpp
src/qml/jsruntime/qv4lookup_p.h
src/qml/jsruntime/qv4managed.cpp
src/qml/jsruntime/qv4managed_p.h
src/qml/jsruntime/qv4object.cpp
src/qml/jsruntime/qv4object_p.h
src/qml/jsruntime/qv4objectproto.cpp
src/qml/jsruntime/qv4property_p.h
src/qml/jsruntime/qv4qobjectwrapper.cpp
src/qml/jsruntime/qv4regexpobject.cpp
src/qml/jsruntime/qv4runtime.cpp
src/qml/jsruntime/qv4runtime_p.h
src/qml/jsruntime/qv4sequenceobject.cpp
src/qml/jsruntime/qv4serialize.cpp
src/qml/jsruntime/qv4string.cpp
src/qml/jsruntime/qv4string_p.h
src/qml/jsruntime/qv4stringobject.cpp
src/qml/qml/qqmllistwrapper.cpp
src/qml/qml/qqmllocale.cpp
src/qml/qml/v8/qv8engine.cpp
src/qml/types/qqmldelegatemodel.cpp
src/qml/types/qqmllistmodel.cpp
src/quick/items/context2d/qquickcontext2d.cpp
src/quick/util/qquickglobal.cpp
tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp

index 48693db..7e0de1c 100644 (file)
@@ -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));
index e8038f1..415fdad 100644 (file)
@@ -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);
index 94983f5..a90cea9 100644 (file)
@@ -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 \
index f157796..5094baf 100644 (file)
@@ -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 (file)
index 0000000..0ec36bd
--- /dev/null
@@ -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 (file)
index 0000000..89628cd
--- /dev/null
@@ -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
index 0fc7f2d..eb8a530 100644 (file)
@@ -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)
index 7e809f9..641be86 100644 (file)
@@ -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);
index 35ed8fe..27c3a31 100644 (file)
@@ -77,6 +77,7 @@ const ManagedVTable ExecutionContext::static_vtbl =
     0,
     isEqualTo,
     0,
+    0,
     "ExecutionContext",
 };
 
index 4538884..10b2590 100644 (file)
@@ -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();
         }
index 787e530..65f053e 100644 (file)
@@ -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())
index a870cda..66dc7b7 100644 (file)
@@ -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)
 {
index 9966d36..93c2e21 100644 (file)
@@ -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);
index 47f9195..c3dec25 100644 (file)
@@ -71,6 +71,7 @@ const ManagedVTable Managed::static_vtbl =
     0,
     isEqualTo,
     0,
+    0,
     "Managed",
 };
 
index 1243921..932dbe8 100644 (file)
@@ -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;
         };
     };
 
index fb56d62..142eae8 100644 (file)
@@ -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());
index a5f118c..b9fc05e 100644 (file)
@@ -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<>
index 6bce8a9..f319b99 100644 (file)
@@ -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);
     }
 
index 9763de5..ff9ea59 100644 (file)
@@ -53,7 +53,7 @@ struct FunctionObject;
 
 struct Property {
     union {
-        Value value;
+        SafeValue value;
         struct {
             FunctionObject *get;
             FunctionObject *set;
index 4293cca..bad2cf3 100644 (file)
@@ -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();
index e8d92ef..1813250 100644 (file)
@@ -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);
 
index 23549fe..91ddfd0 100644 (file)
@@ -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)
index 9524b24..2e252b1 100644 (file)
@@ -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);
index 988762c..feb3806 100644 (file)
@@ -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)));
index b199c86..5f9e24d 100644 (file)
@@ -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);
index b97b23b..df48faf 100644 (file)
@@ -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();
+}
index 7e2824d..bc5b305 100644 (file)
@@ -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;
index 3d90c81..1781ce2 100644 (file)
@@ -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();
index 7b975c2..76c11cb 100644 (file)
@@ -57,7 +57,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine)
       v8(engine)
 {
     setVTable(&static_vtbl);
-    flags &= ~SimpleArray;
+    setArrayType(ArrayData::Custom);
 }
 
 QmlListWrapper::~QmlListWrapper()
index 94cf268..75a366a 100644 (file)
@@ -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());
index 1e7fdbc..c5c2a69 100644 (file)
@@ -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));
index 32ebb93..40150d6 100644 (file)
@@ -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() {}
 
index 7e44102..eeb4aa0 100644 (file)
@@ -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) {
index b6d4f10..c874133 100644 (file)
@@ -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) {
index 152d1f3..5ae74c2 100644 (file)
@@ -398,7 +398,7 @@ public:
         if (!array)
             return QMatrix4x4();
 
-        if (array->arrayLength() != 16)
+        if (array->getLength() != 16)
             return QMatrix4x4();
 
         float matVals[16];
index ea7259c..b11ad2d 100644 (file)
@@ -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);