Rework our simple array implementation
authorLars Knoll <lars.knoll@digia.com>
Tue, 21 Oct 2014 12:54:45 +0000 (14:54 +0200)
committerSimon Hausmann <simon.hausmann@digia.com>
Mon, 27 Oct 2014 14:19:08 +0000 (15:19 +0100)
Implement the simple array as a circular buffer instead
of an array with head room. This fixes a couple of severe
issues with performance and memory management if the array
is being used as a queue.

Task-number: QTBUG-41421
Change-Id: I146ad8a874407c108aa8fe1eae68e9957e154847
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/jsruntime/qv4argumentsobject.cpp
src/qml/jsruntime/qv4arraydata.cpp
src/qml/jsruntime/qv4arraydata_p.h
src/qml/jsruntime/qv4arrayobject.cpp
src/qml/jsruntime/qv4functionobject.cpp
src/qml/jsruntime/qv4lookup.cpp
src/qml/jsruntime/qv4object.cpp
src/qml/jsruntime/qv4object_p.h
src/qml/jsruntime/qv4runtime.cpp

index 9ac279b..d79521c 100644 (file)
@@ -78,7 +78,7 @@ void ArgumentsObject::fullyCreate()
 
     uint numAccessors = qMin((int)context()->d()->function->formalParameterCount(), context()->d()->realArgumentCount);
     uint argCount = qMin(context()->d()->realArgumentCount, context()->d()->callData->argc);
-    ArrayData::realloc(this, ArrayData::Sparse, 0, argCount, true);
+    ArrayData::realloc(this, ArrayData::Sparse, argCount, true);
     context()->d()->engine->requireArgumentsAccessors(numAccessors);
     mappedArguments().ensureIndex(engine(), numAccessors);
     for (uint i = 0; i < (uint)numAccessors; ++i) {
index d58dbb9..469081e 100644 (file)
@@ -87,65 +87,61 @@ const ArrayVTable SparseArrayData::static_vtbl =
     SparseArrayData::length
 };
 
+Q_STATIC_ASSERT(sizeof(ArrayData::Data) == sizeof(SimpleArrayData::Data));
+Q_STATIC_ASSERT(sizeof(ArrayData::Data) == sizeof(SparseArrayData::Data));
 
-void ArrayData::realloc(Object *o, Type newType, uint offset, uint alloc, bool enforceAttributes)
+void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAttributes)
 {
     ArrayData *d = o->arrayData();
 
-    uint oldAlloc = 0;
+    uint alloc = 8;
     uint toCopy = 0;
-    if (alloc < 8)
-        alloc = 8;
+    uint offset = 0;
 
     if (d) {
         bool hasAttrs = d->attrs();
         enforceAttributes |= hasAttrs;
 
-        if (!offset && alloc <= d->alloc() && newType == d->type() && hasAttrs == enforceAttributes)
+        if (requested <= d->alloc() && newType == d->type() && hasAttrs == enforceAttributes)
             return;
 
-        oldAlloc = d->alloc();
         if (d->type() < Sparse) {
-            offset = qMax(offset, static_cast<SimpleArrayData *>(d)->offset());
+            offset = static_cast<SimpleArrayData *>(d)->d()->offset;
             toCopy = static_cast<SimpleArrayData *>(d)->len();
         } else {
-            Q_ASSERT(!offset);
             toCopy = d->alloc();
-            newType = Sparse;
         }
+        if (d->type() > newType)
+            newType = d->type();
     }
     if (enforceAttributes && newType == Simple)
         newType = Complex;
 
-    alloc = qMax(alloc, 2*oldAlloc) + offset;
-    size_t size = alloc*sizeof(Value);
+    while (alloc < requested)
+        alloc *= 2;
+    size_t size = sizeof(ArrayData::Data) + alloc*sizeof(Value);
     if (enforceAttributes)
         size += alloc*sizeof(PropertyAttributes);
 
+    ArrayData *newData;
     if (newType < Sparse) {
-        size += sizeof(SimpleArrayData::Data);
-        SimpleArrayData *newData = static_cast<SimpleArrayData *>(o->engine()->memoryManager->allocManaged(size));
-        new (newData->d()) SimpleArrayData::Data(o->engine());
-        newData->setAlloc(alloc - offset);
-        newData->setType(newType);
-        newData->setArrayData(reinterpret_cast<Value *>(newData->d() + 1) + offset);
-        newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->arrayData() + alloc) + offset : 0);
-        newData->offset() = offset;
-        newData->len() = d ? static_cast<SimpleArrayData *>(d)->len() : 0;
-        o->setArrayData(newData);
+        SimpleArrayData *n = static_cast<SimpleArrayData *>(o->engine()->memoryManager->allocManaged(size));
+        new (n->d()) SimpleArrayData::Data(o->engine());
+        n->d()->offset = 0;
+        n->len() = d ? static_cast<SimpleArrayData *>(d)->len() : 0;
+        newData = n;
     } else {
-        size += sizeof(SparseArrayData::Data);
-        SparseArrayData *newData = static_cast<SparseArrayData *>(o->engine()->memoryManager->allocManaged(size));
-        new (newData->d()) SparseArrayData::Data(o->engine());
-        newData->setAlloc(alloc);
-        newData->setType(newType);
-        newData->setArrayData(reinterpret_cast<Value *>(newData->d() + 1));
-        newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->arrayData() + alloc) : 0);
-        o->setArrayData(newData);
+        SparseArrayData *n = static_cast<SparseArrayData *>(o->engine()->memoryManager->allocManaged(size));
+        new (n->d()) SparseArrayData::Data(o->engine());
+        newData = n;
     }
+    newData->setAlloc(alloc);
+    newData->setType(newType);
+    newData->d()->arrayData = reinterpret_cast<Value *>(newData->d() + 1);
+    newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->arrayData + alloc) : 0);
+    o->setArrayData(newData);
 
     if (d) {
-        memcpy(o->arrayData()->arrayData(), d->arrayData(), sizeof(Value)*toCopy);
         if (enforceAttributes) {
             if (d->attrs())
                 memcpy(o->arrayData()->attrs(), d->attrs(), sizeof(PropertyAttributes)*toCopy);
@@ -153,56 +149,56 @@ void ArrayData::realloc(Object *o, Type newType, uint offset, uint alloc, bool e
                 for (uint i = 0; i < toCopy; ++i)
                     o->arrayData()->attrs()[i] = Attr_Data;
         }
+
+        if (toCopy > d->d()->alloc - offset) {
+            uint copyFromStart = toCopy - (d->d()->alloc - offset);
+            memcpy(o->arrayData()->d()->arrayData + toCopy - copyFromStart, d->d()->arrayData, sizeof(Value)*copyFromStart);
+            toCopy -= copyFromStart;
+        }
+        memcpy(o->arrayData()->d()->arrayData, d->d()->arrayData + offset, sizeof(Value)*toCopy);
     }
 
     if (newType != Sparse)
         return;
 
-    SparseArrayData *newData = static_cast<SparseArrayData *>(o->arrayData());
+    SparseArrayData *sparse = static_cast<SparseArrayData *>(o->arrayData());
+
+    uint *lastFree;
     if (d && d->type() == Sparse) {
         SparseArrayData *old = static_cast<SparseArrayData *>(d);
-        newData->setSparse(old->sparse());
+        sparse->setSparse(old->sparse());
         old->setSparse(0);
-        newData->freeList() = old->freeList();
+        sparse->freeList() = old->freeList();
+        lastFree = &sparse->freeList();
     } else {
-        newData->setSparse(new SparseArray);
-        uint *lastFree = &newData->freeList();
+        sparse->setSparse(new SparseArray);
+        lastFree = &sparse->freeList();
         for (uint i = 0; i < toCopy; ++i) {
-            if (!newData->arrayData()[i].isEmpty()) {
-                SparseArrayNode *n = newData->sparse()->insert(i);
+            if (!sparse->arrayData()[i].isEmpty()) {
+                SparseArrayNode *n = sparse->sparse()->insert(i);
                 n->value = i;
             } else {
                 *lastFree = i;
-                newData->arrayData()[i].tag = Value::Empty_Type;
-                lastFree = &newData->arrayData()[i].uint_32;
+                sparse->arrayData()[i].tag = Value::Empty_Type;
+                lastFree = &sparse->arrayData()[i].uint_32;
             }
         }
     }
 
-    uint *lastFree = &newData->freeList();
-    for (uint i = toCopy; i < newData->alloc(); ++i) {
-        *lastFree = i;
-        newData->arrayData()[i].tag = Value::Empty_Type;
-        lastFree = &newData->arrayData()[i].uint_32;
+    if (toCopy < sparse->alloc()) {
+        for (uint i = toCopy; i < sparse->alloc(); ++i) {
+            *lastFree = i;
+            sparse->arrayData()[i].tag = Value::Empty_Type;
+            lastFree = &sparse->arrayData()[i].uint_32;
+        }
+        *lastFree = UINT_MAX;
     }
-    *lastFree = newData->alloc();
-
     // ### Could explicitly free the old data
 }
 
-
-void SimpleArrayData::getHeadRoom(Object *o)
-{
-    SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
-    Q_ASSERT(dd);
-    Q_ASSERT(!dd->offset());
-    uint offset = qMax(dd->len() >> 2, (uint)16);
-    realloc(o, Simple, offset, 0, false);
-}
-
 ArrayData *SimpleArrayData::reallocate(Object *o, uint n, bool enforceAttributes)
 {
-    realloc(o, Simple, 0, n, enforceAttributes);
+    realloc(o, Simple, n, enforceAttributes);
     return o->arrayData();
 }
 
@@ -211,7 +207,7 @@ void ArrayData::ensureAttributes(Object *o)
     if (o->arrayData() && o->arrayData()->attrs())
         return;
 
-    ArrayData::realloc(o, Simple, 0, 0, true);
+    ArrayData::realloc(o, Simple, 0, true);
 }
 
 
@@ -220,7 +216,7 @@ void SimpleArrayData::markObjects(Managed *d, ExecutionEngine *e)
     SimpleArrayData *dd = static_cast<SimpleArrayData *>(d);
     uint l = dd->len();
     for (uint i = 0; i < l; ++i)
-        dd->arrayData()[i].mark(e);
+        dd->data(i).mark(e);
 }
 
 ReturnedValue SimpleArrayData::get(const ArrayData *d, uint index)
@@ -228,7 +224,7 @@ ReturnedValue SimpleArrayData::get(const ArrayData *d, uint index)
     const SimpleArrayData *dd = static_cast<const SimpleArrayData *>(d);
     if (index >= dd->len())
         return Primitive::emptyValue().asReturnedValue();
-    return dd->arrayData()[index].asReturnedValue();
+    return dd->data(index).asReturnedValue();
 }
 
 bool SimpleArrayData::put(Object *o, uint index, ValueRef value)
@@ -236,7 +232,7 @@ bool SimpleArrayData::put(Object *o, uint index, ValueRef value)
     SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
     Q_ASSERT(index >= dd->len() || !dd->attrs() || !dd->attrs()[index].isAccessor());
     // ### honour attributes
-    dd->arrayData()[index] = value;
+    dd->data(index) = value;
     if (index >= dd->len()) {
         if (dd->attrs())
             dd->attrs()[index] = Attr_Data;
@@ -252,12 +248,12 @@ bool SimpleArrayData::del(Object *o, uint index)
         return true;
 
     if (!dd->attrs() || dd->attrs()[index].isConfigurable()) {
-        dd->arrayData()[index] = Primitive::emptyValue();
+        dd->data(index) = Primitive::emptyValue();
         if (dd->attrs())
             dd->attrs()[index] = Attr_Data;
         return true;
     }
-    if (dd->arrayData()[index].isEmpty())
+    if (dd->data(index).isEmpty())
         return true;
     return false;
 }
@@ -276,20 +272,14 @@ void SimpleArrayData::push_front(Object *o, Value *values, uint n)
 {
     SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
     Q_ASSERT(!dd->attrs());
-    for (int i = n - 1; i >= 0; --i) {
-        if (!dd->offset()) {
-            getHeadRoom(o);
-            dd = static_cast<SimpleArrayData *>(o->arrayData());
-        }
-
-
-        --dd->offset();
-        --dd->arrayData();
-        ++dd->len();
-        ++dd->alloc();
-        *dd->arrayData() = values[i].asReturnedValue();
+    if (dd->len() + n > dd->alloc()) {
+        realloc(o, Simple, dd->len() + n, false);
+        dd = static_cast<SimpleArrayData *>(o->arrayData());
     }
-
+    dd->d()->offset = (dd->d()->offset - n) % dd->d()->alloc;
+    dd->len() += n;
+    for (uint i = 0; i < n; ++i)
+        dd->data(i) = values[i].asReturnedValue();
 }
 
 ReturnedValue SimpleArrayData::pop_front(Object *o)
@@ -299,11 +289,9 @@ ReturnedValue SimpleArrayData::pop_front(Object *o)
     if (!dd->len())
         return Encode::undefined();
 
-    ReturnedValue v = dd->arrayData()[0].isEmpty() ? Encode::undefined() : dd->arrayData()[0].asReturnedValue();
-    ++dd->offset();
-    ++dd->arrayData();
+    ReturnedValue v = dd->data(0).isEmpty() ? Encode::undefined() : dd->data(0).asReturnedValue();
+    dd->d()->offset = (dd->d()->offset + 1) % dd->d()->alloc;
     --dd->len();
-    --dd->alloc();
     return v;
 }
 
@@ -313,19 +301,17 @@ uint SimpleArrayData::truncate(Object *o, uint newLen)
     if (dd->len() < newLen)
         return newLen;
 
-    if (dd->attrs()) {
-        Value *it = dd->arrayData() + dd->len();
-        const Value *begin = dd->arrayData() + newLen;
-        while (--it >= begin) {
-            if (!it->isEmpty() && !dd->attrs()[it - dd->arrayData()].isConfigurable()) {
-                newLen = it - dd->arrayData() + 1;
-                break;
-            }
-            *it = Primitive::emptyValue();
-        }
+    if (!dd->attrs()) {
+        dd->len() = newLen;
+        return newLen;
     }
-    dd->len() = newLen;
-    return newLen;
+
+    while (dd->len() > newLen) {
+        if (!dd->data(dd->len() - 1).isEmpty() && !dd->attrs()[dd->len() - 1].isConfigurable())
+            return dd->len();
+        --dd->len();
+    }
+    return dd->len();
 }
 
 uint SimpleArrayData::length(const ArrayData *d)
@@ -341,9 +327,9 @@ bool SimpleArrayData::putArray(Object *o, uint index, Value *values, uint n)
         dd = static_cast<SimpleArrayData *>(o->arrayData());
     }
     for (uint i = dd->len(); i < index; ++i)
-        dd->arrayData()[i] = Primitive::emptyValue();
+        dd->data(i) = Primitive::emptyValue();
     for (uint i = 0; i < n; ++i)
-        dd->arrayData()[index + i] = values[i];
+        dd->data(index + i) = values[i];
     dd->len() = qMax(dd->len(), index + n);
     return true;
 }
@@ -385,7 +371,7 @@ void SparseArrayData::markObjects(Managed *d, ExecutionEngine *e)
 
 ArrayData *SparseArrayData::reallocate(Object *o, uint n, bool enforceAttributes)
 {
-    realloc(o, Sparse, 0, n, enforceAttributes);
+    realloc(o, Sparse, n, enforceAttributes);
     return o->arrayData();
 }
 
@@ -397,12 +383,14 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot)
     if (doubleSlot) {
         uint *last = &dd->freeList();
         while (1) {
-            if (*last + 1 >= dd->alloc()) {
+            if (*last == UINT_MAX) {
                 reallocate(o, o->arrayData()->alloc() + 2, true);
                 dd = static_cast<SparseArrayData *>(o->arrayData());
                 last = &dd->freeList();
+                Q_ASSERT(*last != UINT_MAX);
             }
 
+            Q_ASSERT(dd->arrayData()[*last].uint_32 != *last);
             if (dd->arrayData()[*last].uint_32 == (*last + 1)) {
                 // found two slots in a row
                 uint idx = *last;
@@ -413,11 +401,12 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot)
             last = &dd->arrayData()[*last].uint_32;
         }
     } else {
-        if (dd->alloc() == dd->freeList()) {
-            reallocate(o, o->arrayData()->alloc() + 2, false);
+        if (dd->freeList() == UINT_MAX) {
+            reallocate(o, o->arrayData()->alloc() + 1, false);
             dd = static_cast<SparseArrayData *>(o->arrayData());
         }
         uint idx = dd->freeList();
+        Q_ASSERT(idx != UINT_MAX);
         dd->freeList() = dd->arrayData()[idx].uint_32;
         if (dd->attrs())
             dd->attrs()[idx] = Attr_Data;
@@ -427,10 +416,11 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot)
 
 ReturnedValue SparseArrayData::get(const ArrayData *d, uint index)
 {
-    SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse()->findNode(index);
+    const SparseArrayData *s = static_cast<const SparseArrayData *>(d);
+    SparseArrayNode *n = s->sparse()->findNode(index);
     if (!n)
         return Primitive::emptyValue().asReturnedValue();
-    return d->arrayData()[n->value].asReturnedValue();
+    return s->arrayData()[n->value].asReturnedValue();
 }
 
 bool SparseArrayData::put(Object *o, uint index, ValueRef value)
@@ -438,13 +428,14 @@ bool SparseArrayData::put(Object *o, uint index, ValueRef value)
     if (value->isEmpty())
         return true;
 
-    SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData())->sparse()->insert(index);
+    const SparseArrayData *s = static_cast<const SparseArrayData *>(o->arrayData());
+    SparseArrayNode *n = s->sparse()->insert(index);
     Q_ASSERT(n->value == UINT_MAX || !o->arrayData()->attrs() || !o->arrayData()->attrs()[n->value].isAccessor());
     if (n->value == UINT_MAX)
         n->value = allocate(o);
-    o->arrayData()->arrayData()[n->value] = value;
-    if (o->arrayData()->attrs())
-        o->arrayData()->attrs()[n->value] = Attr_Data;
+    s->arrayData()[n->value] = value;
+    if (s->attrs())
+        s->attrs()[n->value] = Attr_Data;
     return true;
 }
 
@@ -469,12 +460,12 @@ bool SparseArrayData::del(Object *o, uint index)
 
     if (isAccessor) {
         // free up both indices
-        dd->arrayData()[pidx + 1].tag = Value::Undefined_Type;
+        dd->arrayData()[pidx + 1].tag = Value::Empty_Type;
         dd->arrayData()[pidx + 1].uint_32 = static_cast<SparseArrayData *>(dd)->freeList();
         dd->arrayData()[pidx].tag = Value::Undefined_Type;
         dd->arrayData()[pidx].uint_32 = pidx + 1;
     } else {
-        dd->arrayData()[pidx].tag = Value::Undefined_Type;
+        dd->arrayData()[pidx].tag = Value::Empty_Type;
         dd->arrayData()[pidx].uint_32 = static_cast<SparseArrayData *>(dd)->freeList();
     }
 
@@ -509,21 +500,23 @@ PropertyAttributes SparseArrayData::attribute(const ArrayData *d, uint index)
 
 void SparseArrayData::push_front(Object *o, Value *values, uint n)
 {
+    SparseArrayData *d = static_cast<SparseArrayData *>(o->arrayData());
     Q_ASSERT(!o->arrayData()->attrs());
     for (int i = n - 1; i >= 0; --i) {
         uint idx = allocate(o);
-        o->arrayData()->arrayData()[idx] = values[i];
-        static_cast<SparseArrayData *>(o->arrayData())->sparse()->push_front(idx);
+        d->arrayData()[idx] = values[i];
+        d->sparse()->push_front(idx);
     }
 }
 
 ReturnedValue SparseArrayData::pop_front(Object *o)
 {
+    SparseArrayData *d = static_cast<SparseArrayData *>(o->arrayData());
     Q_ASSERT(!o->arrayData()->attrs());
-    uint idx = static_cast<SparseArrayData *>(o->arrayData())->sparse()->pop_front();
+    uint idx = d->sparse()->pop_front();
     ReturnedValue v;
     if (idx != UINT_MAX) {
-        v = o->arrayData()->arrayData()[idx].asReturnedValue();
+        v = d->arrayData()[idx].asReturnedValue();
         free(o->arrayData(), idx);
     } else {
         v = Encode::undefined();
@@ -591,21 +584,30 @@ uint ArrayData::append(Object *obj, const ArrayObject *otherObj, uint n)
     uint oldSize = obj->getLength();
 
     if (other->isSparse()) {
+        const SparseArrayData *os = static_cast<const SparseArrayData *>(other);
         if (otherObj->hasAccessorProperty() && other->hasAttributes()) {
             Scope scope(obj->engine());
             ScopedValue v(scope);
-            for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse()->begin();
-                 it != static_cast<const SparseArrayData *>(other)->sparse()->end(); it = it->nextNode()) {
-                v = otherObj->getValue(reinterpret_cast<Property *>(other->arrayData() + it->value), other->attrs()[it->value]);
+            for (const SparseArrayNode *it = os->sparse()->begin();
+                 it != os->sparse()->end(); it = it->nextNode()) {
+                v = otherObj->getValue(reinterpret_cast<Property *>(os->arrayData() + it->value), other->attrs()[it->value]);
                 obj->arraySet(oldSize + it->key(), v);
             }
         } else {
             for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse()->begin();
-                 it != static_cast<const SparseArrayData *>(other)->sparse()->end(); it = it->nextNode())
-                obj->arraySet(oldSize + it->key(), ValueRef(other->arrayData()[it->value]));
+                 it != os->sparse()->end(); it = it->nextNode())
+                obj->arraySet(oldSize + it->key(), ValueRef(os->arrayData()[it->value]));
         }
     } else {
-        obj->arrayPut(oldSize, other->arrayData(), n);
+        const SimpleArrayData *os = static_cast<const SimpleArrayData *>(other);
+        uint toCopy = n;
+        uint chunk = toCopy;
+        if (chunk > os->alloc() - os->d()->offset)
+            chunk -= os->alloc() - os->d()->offset;
+        obj->arrayPut(oldSize, os->arrayData() + os->d()->offset, chunk);
+        toCopy -= chunk;
+        if (toCopy)
+            obj->arrayPut(oldSize + chunk, os->arrayData(), toCopy);
     }
 
     return oldSize + n;
@@ -623,18 +625,19 @@ Property *ArrayData::insert(Object *o, uint index, bool isAccessor)
             if (index >= d->len()) {
                 // mark possible hole in the array
                 for (uint i = d->len(); i < index; ++i)
-                    d->arrayData()[i] = Primitive::emptyValue();
+                    d->data(i) = Primitive::emptyValue();
                 d->len() = index + 1;
             }
-            return reinterpret_cast<Property *>(o->arrayData()->arrayData() + index);
+            return reinterpret_cast<Property *>(d->d()->arrayData + d->realIndex(index));
         }
     }
 
     o->initSparseArray();
-    SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData())->sparse()->insert(index);
+    SparseArrayData *s = static_cast<SparseArrayData *>(o->arrayData());
+    SparseArrayNode *n = s->sparse()->insert(index);
     if (n->value == UINT_MAX)
         n->value = SparseArrayData::allocate(o, isAccessor);
-    return reinterpret_cast<Property *>(o->arrayData()->arrayData() + n->value);
+    return reinterpret_cast<Property *>(s->arrayData() + n->value);
 }
 
 
@@ -757,7 +760,7 @@ void ArrayData::sort(ExecutionContext *context, Object *thisObject, const ValueR
             return;
 
         thisObject->setArrayData(0);
-        ArrayData::realloc(thisObject, ArrayData::Simple, 0, sparse->sparse()->nEntries(), sparse->attrs() ? true : false);
+        ArrayData::realloc(thisObject, ArrayData::Simple, sparse->sparse()->nEntries(), sparse->attrs() ? true : false);
         SimpleArrayData *d = static_cast<SimpleArrayData *>(thisObject->arrayData());
 
         SparseArrayNode *n = sparse->sparse()->begin();
@@ -768,7 +771,7 @@ void ArrayData::sort(ExecutionContext *context, Object *thisObject, const ValueR
                     break;
 
                 PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data;
-                d->arrayData()[i] = thisObject->getValue(reinterpret_cast<Property *>(sparse->arrayData() + n->value), a);
+                d->data(i) = thisObject->getValue(reinterpret_cast<Property *>(sparse->arrayData() + n->value), a);
                 d->attrs()[i] = a.isAccessor() ? Attr_Data : a;
 
                 n = n->nextNode();
@@ -778,7 +781,7 @@ void ArrayData::sort(ExecutionContext *context, Object *thisObject, const ValueR
             while (n != sparse->sparse()->end()) {
                 if (n->value >= len)
                     break;
-                d->arrayData()[i] = sparse->arrayData()[n->value];
+                d->data(i) = sparse->arrayData()[n->value];
                 n = n->nextNode();
                 ++i;
             }
@@ -805,13 +808,13 @@ void ArrayData::sort(ExecutionContext *context, Object *thisObject, const ValueR
 
         // sort empty values to the end
         for (uint i = 0; i < len; i++) {
-            if (thisObject->arrayData()->arrayData()[i].isEmpty()) {
+            if (d->data(i).isEmpty()) {
                 while (--len > i)
-                    if (!thisObject->arrayData()->arrayData()[len].isEmpty())
+                    if (!d->data(len).isEmpty())
                         break;
-                Q_ASSERT(!thisObject->arrayData()->attrs() || !thisObject->arrayData()->attrs()[len].isAccessor());
-                thisObject->arrayData()->arrayData()[i] = thisObject->arrayData()->arrayData()[len];
-                thisObject->arrayData()->arrayData()[len] = Primitive::emptyValue();
+                Q_ASSERT(!d->attrs() || !d->attrs()[len].isAccessor());
+                d->data(i) = d->data(len);
+                d->data(len) = Primitive::emptyValue();
             }
         }
 
@@ -822,7 +825,7 @@ void ArrayData::sort(ExecutionContext *context, Object *thisObject, const ValueR
 
     ArrayElementLessThan lessThan(context, thisObject, comparefn);
 
-    Value *begin = thisObject->arrayData()->arrayData();
+    Value *begin = thisObject->arrayData()->d()->arrayData;
     sortHelper(begin, begin + len, *begin, lessThan);
 
 #ifdef CHECK_SPARSE_ARRAYS
index 3b04fe9..cd080ba 100644 (file)
@@ -90,6 +90,14 @@ struct Q_QML_EXPORT ArrayData : public Managed
         uint alloc;
         Type type;
         PropertyAttributes *attrs;
+        union {
+            uint len;
+            uint freeList;
+        };
+        union {
+            uint offset;
+            SparseArray *sparse;
+        };
         Value *arrayData;
     };
     V4_MANAGED(Managed)
@@ -103,7 +111,6 @@ struct Q_QML_EXPORT ArrayData : public Managed
     void setAttrs(PropertyAttributes *a) { d()->attrs = a; }
     Value *arrayData() const { return d()->arrayData; }
     Value *&arrayData() { return d()->arrayData; }
-    void setArrayData(Value *v) { d()->arrayData = v; }
 
     const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass()->vtable); }
     bool isSparse() const { return this && type() == Sparse; }
@@ -136,7 +143,7 @@ struct Q_QML_EXPORT ArrayData : public Managed
     inline Property *getProperty(uint index) const;
 
     static void ensureAttributes(Object *o);
-    static void realloc(Object *o, Type newType, uint offset, uint alloc, bool enforceAttributes);
+    static void realloc(Object *o, Type newType, uint alloc, bool enforceAttributes);
 
     static void sort(ExecutionContext *context, Object *thisObject, const ValueRef comparefn, uint dataLen);
     static uint append(Object *obj, const ArrayObject *otherObj, uint n);
@@ -150,17 +157,25 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData
         Data(ExecutionEngine *engine)
             : ArrayData::Data(engine->simpleArrayDataClass)
         {}
-        uint len;
-        uint offset;
     };
     V4_ARRAYDATA
 
+    uint realIndex(uint index) const { return (index + d()->offset) % d()->alloc; }
+    Value data(uint index) const { return d()->arrayData[realIndex(index)]; }
+    Value &data(uint index) { return d()->arrayData[realIndex(index)]; }
+
+    Property *getProperty(uint index) const {
+        if (index >= len())
+            return 0;
+        index = realIndex(index);
+        if (d()->arrayData[index].isEmpty())
+            return 0;
+        return reinterpret_cast<Property *>(d()->arrayData + index);
+    }
+
     uint &len() { return d()->len; }
     uint len() const { return d()->len; }
-    uint &offset() { return d()->offset; }
-    uint offset() const { return d()->offset; }
 
-    static void getHeadRoom(Object *o);
     static ArrayData *reallocate(Object *o, uint n, bool enforceAttributes);
 
     static void markObjects(Managed *d, ExecutionEngine *e);
@@ -183,9 +198,6 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData
         Data(ExecutionEngine *engine)
             : ArrayData::Data(engine->emptyClass)
         { setVTable(staticVTable()); }
-
-        uint freeList;
-        SparseArray *sparse;
     };
     V4_ARRAYDATA
 
@@ -220,14 +232,13 @@ inline Property *ArrayData::getProperty(uint index) const
         return 0;
     if (type() != Sparse) {
         const SimpleArrayData *that = static_cast<const SimpleArrayData *>(this);
-        if (index >= that->len() || arrayData()[index].isEmpty())
-            return 0;
-        return reinterpret_cast<Property *>(arrayData() + index);
+        return that->getProperty(index);
     } else {
-        SparseArrayNode *n = static_cast<const SparseArrayData *>(this)->sparse()->findNode(index);
+        const SparseArrayData *that = static_cast<const SparseArrayData *>(this);
+        SparseArrayNode *n = that->sparse()->findNode(index);
         if (!n)
             return 0;
-        return reinterpret_cast<Property *>(arrayData() + n->value);
+        return reinterpret_cast<Property *>(that->arrayData() + n->value);
     }
 }
 
index 7515a83..eaf4742 100644 (file)
@@ -614,20 +614,17 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
         return Encode(-1);
     } else {
         Q_ASSERT(instance->arrayType() == ArrayData::Simple || instance->arrayType() == ArrayData::Complex);
-        if (len > instance->arrayData()->length())
-            len = instance->arrayData()->length();
-        Value *val = instance->arrayData()->arrayData();
-        Value *end = val + len;
-        val += fromIndex;
-        while (val < end) {
-            if (!val->isEmpty()) {
-                value = *val;
-                if (scope.hasException())
-                    return Encode::undefined();
-                if (RuntimeHelpers::strictEqual(value, searchValue))
-                    return Encode((uint)(val - instance->arrayData()->arrayData()));
-            }
-            ++val;
+        SimpleArrayData *sa = static_cast<SimpleArrayData *>(instance->arrayData());
+        if (len > sa->len())
+            len = sa->len();
+        uint idx = fromIndex;
+        while (idx < len) {
+            value = sa->data(idx);
+            if (scope.hasException())
+                return Encode::undefined();
+            if (RuntimeHelpers::strictEqual(value, searchValue))
+                return Encode(idx);
+            ++idx;
         }
     }
     return Encode(-1);
index 84b0eb1..67f2a0c 100644 (file)
@@ -298,9 +298,9 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx)
             for (quint32 i = 0; i < len; ++i)
                 callData->args[i] = arr->getIndexed(i);
         } else {
-            int alen = qMin(len, arr->arrayData()->length());
-            if (alen)
-                memcpy(callData->args, arr->arrayData()->arrayData(), alen*sizeof(Value));
+            uint alen = qMin(len, arr->arrayData()->length());
+            for (uint i = 0; i < alen; ++i)
+                callData->args[i] = static_cast<SimpleArrayData *>(arr->arrayData())->data(i);
             for (quint32 i = alen; i < len; ++i)
                 callData->args[i] = Primitive::undefinedValue();
         }
index 6d4f05d..4f2ec83 100644 (file)
@@ -164,9 +164,10 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const ValueRef object, c
 
     Object *o = object->objectValue();
     if (o->arrayData() && o->arrayData()->type() == ArrayData::Simple) {
-        if (idx < static_cast<SimpleArrayData *>(o->arrayData())->len())
-            if (!o->arrayData()->arrayData()[idx].isEmpty())
-                return o->arrayData()->arrayData()[idx].asReturnedValue();
+        SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData());
+        if (idx < s->len())
+            if (!s->data(idx).isEmpty())
+                return s->data(idx).asReturnedValue();
     }
 
     return indexedGetterFallback(l, object, index);
@@ -197,8 +198,8 @@ void Lookup::indexedSetterFallback(Lookup *l, const ValueRef object, const Value
     if (idx < UINT_MAX) {
         if (o->arrayData() && o->arrayData()->type() == ArrayData::Simple) {
             SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData());
-            if (s && idx < s->len() && !s->arrayData()[idx].isEmpty()) {
-                s->arrayData()[idx] = value;
+            if (idx < s->len() && !s->data(idx).isEmpty()) {
+                s->data(idx) = value;
                 return;
             }
         }
@@ -221,8 +222,8 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const ValueRef object, const Valu
     Object *o = object->objectValue();
     if (o->arrayData() && o->arrayData()->type() == ArrayData::Simple) {
         SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData());
-        if (idx < s->len() && !s->arrayData()[idx].isEmpty()) {
-            s->arrayData()[idx] = v;
+        if (idx < s->len() && !s->data(idx).isEmpty()) {
+            s->data(idx) = v;
             return;
         }
     }
index 6d78c89..1b25320 100644 (file)
@@ -533,7 +533,8 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, String *&name, uint
             while (it->arrayNode != o->sparseEnd()) {
                 int k = it->arrayNode->key();
                 uint pidx = it->arrayNode->value;
-                Property *p = reinterpret_cast<Property *>(o->arrayData()->arrayData() + pidx);
+                SparseArrayData *sa = static_cast<SparseArrayData *>(o->arrayData());
+                Property *p = reinterpret_cast<Property *>(sa->arrayData() + pidx);
                 it->arrayNode = it->arrayNode->nextNode();
                 PropertyAttributes a = o->arrayData()->attributes(k);
                 if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
@@ -549,14 +550,15 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, String *&name, uint
         }
         // dense arrays
         while (it->arrayIndex < o->arrayData()->length()) {
-            Value *val = o->arrayData()->arrayData() + it->arrayIndex;
+            SimpleArrayData *sa = static_cast<SimpleArrayData *>(o->arrayData());
+            Value &val = sa->data(it->arrayIndex);
             PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex);
             ++it->arrayIndex;
-            if (!val->isEmpty()
+            if (!val.isEmpty()
                 && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
                 *index = it->arrayIndex - 1;
                 *attrs = a;
-                pd->value = *val;
+                pd->value = val;
                 return;
             }
         }
@@ -1073,14 +1075,15 @@ void Object::copyArrayData(Object *other)
     } else if (other->hasAccessorProperty() && other->arrayData()->attrs() && other->arrayData()->isSparse()){
         // do it the slow way
         ScopedValue v(scope);
-        for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other->arrayData())->sparse()->begin();
-             it != static_cast<const SparseArrayData *>(other->arrayData())->sparse()->end(); it = it->nextNode()) {
-            v = other->getValue(reinterpret_cast<Property *>(other->arrayData()->arrayData() + it->value), other->arrayData()->attrs()[it->value]);
+        const SparseArrayData *osa = static_cast<const SparseArrayData *>(other->arrayData());
+        for (const SparseArrayNode *it = osa->sparse()->begin();
+             it != osa->sparse()->end(); it = it->nextNode()) {
+            v = other->getValue(reinterpret_cast<Property *>(osa->arrayData() + it->value), other->arrayData()->attrs()[it->value]);
             arraySet(it->key(), v);
         }
     } else {
         Q_ASSERT(!arrayData() && other->arrayData());
-        ArrayData::realloc(this, other->arrayData()->type(), 0, other->arrayData()->alloc(), other->arrayData()->attrs());
+        ArrayData::realloc(this, other->arrayData()->type(), other->arrayData()->alloc(), false);
         if (other->arrayType() == ArrayData::Sparse) {
             SparseArrayData *od = static_cast<SparseArrayData *>(other->arrayData());
             SparseArrayData *dd = static_cast<SparseArrayData *>(arrayData());
@@ -1089,9 +1092,9 @@ void Object::copyArrayData(Object *other)
         } else {
             SimpleArrayData *d = static_cast<SimpleArrayData *>(arrayData());
             d->len() = static_cast<SimpleArrayData *>(other->arrayData())->len();
-            d->offset() = 0;
+            d->d()->offset = static_cast<SimpleArrayData *>(other->arrayData())->d()->offset;
         }
-        memcpy(arrayData()->arrayData(), other->arrayData()->arrayData(), arrayData()->alloc()*sizeof(Value));
+        memcpy(arrayData()->d()->arrayData, other->arrayData()->d()->arrayData, arrayData()->alloc()*sizeof(Value));
     }
     setArrayLengthUnchecked(other->getLength());
 }
@@ -1132,7 +1135,7 @@ void Object::initSparseArray()
     if (arrayType() == ArrayData::Sparse)
         return;
 
-    ArrayData::realloc(this, ArrayData::Sparse, 0, 0, false);
+    ArrayData::realloc(this, ArrayData::Sparse, 0, false);
 }
 
 
index 77debf1..86e4e78 100644 (file)
@@ -222,12 +222,12 @@ public:
     }
 
     inline void arrayReserve(uint n) {
-        ArrayData::realloc(this, ArrayData::Simple, 0, n, false);
+        ArrayData::realloc(this, ArrayData::Simple, n, false);
     }
 
     void arrayCreate() {
         if (!arrayData())
-            ArrayData::realloc(this, ArrayData::Simple, 0, 0, false);
+            ArrayData::realloc(this, ArrayData::Simple, 0, false);
 #ifdef CHECK_SPARSE_ARRAYS
         initSparseArray();
 #endif
index 55047e1..5086113 100644 (file)
@@ -606,8 +606,8 @@ void Runtime::setElement(ExecutionContext *ctx, const ValueRef object, const Val
     if (idx < UINT_MAX) {
         if (o->arrayType() == ArrayData::Simple) {
             SimpleArrayData *s = static_cast<SimpleArrayData *>(o->arrayData());
-            if (s && idx < s->len() && !s->arrayData()[idx].isEmpty()) {
-                s->arrayData()[idx] = value;
+            if (s && idx < s->len() && !s->data(idx).isEmpty()) {
+                s->data(idx) = value;
                 return;
             }
         }