Save memory on array data
authorLars Knoll <lars.knoll@digia.com>
Thu, 9 Jan 2014 10:05:08 +0000 (11:05 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 20 Jan 2014 20:14:27 +0000 (21:14 +0100)
Store a simple vector of Values in the array data,
instead of a Vector of Property's. This halfes the
memory consumption on 64bit and simplifies our code.

If an indexed property gets converted to an accessor
property, we simply convert the ArrayData into a
SparseArrayData.

Add support in SparseArrayData to allocate double slots
(two Value's) to hold a full Property in case someone
sets an accessor on an indexed property.

Some methods still return a Property*, but this is safe, as
only the first Value in the Property pointer will ever get
accessed if the Property doesn't contain an accessor.

Change-Id: Ic9b0f309b09a2772a328d947a10faaf3be9fe56f
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
14 files changed:
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/qv4object.cpp
src/qml/jsruntime/qv4object_p.h
src/qml/jsruntime/qv4objectproto.cpp
src/qml/jsruntime/qv4property_p.h
src/qml/jsruntime/qv4runtime.cpp
src/qml/jsruntime/qv4sparsearray.cpp
src/qml/jsruntime/qv4sparsearray_p.h
src/qml/jsruntime/qv4stringobject.cpp
tests/manual/v4/sparsearraytest.js

index 5094baf..fccfd50 100644 (file)
@@ -69,6 +69,7 @@ ArgumentsObject::ArgumentsObject(CallContext *context)
         arrayData->setLength(context->callData->argc);
         fullyCreated = true;
     } else {
+        hasAccessorProperty = 1;
         Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee));
         memberData[CalleePropertyIndex].value = context->function->asReturnedValue();
     }
@@ -92,14 +93,13 @@ void ArgumentsObject::fullyCreate()
     uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount);
     uint argCount = qMin(context->realArgumentCount, context->callData->argc);
     arrayReserve(argCount);
+    initSparseArray();
     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->setAttributes(i, Attr_Accessor);
+        arraySet(i, context->engine->argumentsAccessors.at(i), Attr_Accessor);
     }
-    arrayData->setLength(numAccessors);
     arrayData->put(numAccessors, context->callData->args + numAccessors, argCount - numAccessors);
     for (uint i = numAccessors; i < argCount; ++i)
         arrayData->setAttributes(i, Attr_Data);
@@ -124,6 +124,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
         map = *pd;
         mapAttrs = arrayData->attributes(index);
         arrayData->setAttributes(index, Attr_Data);
+        pd = arrayData->getProperty(index);
         pd->value = mappedArguments.at(index);
     }
 
@@ -139,8 +140,9 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
         map.setter()->call(callData);
 
         if (attrs.isWritable()) {
-            *pd = map;
             arrayData->setAttributes(index, mapAttrs);
+            pd = arrayData->getProperty(index);
+            *pd = map;
         }
     }
 
index 0ec36bd..09e9a7f 100644 (file)
@@ -82,8 +82,8 @@ 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));
+    SafeValue *newArray = new SafeValue[d->offset + d->alloc];
+    memcpy(newArray + d->offset, d->data, d->len*sizeof(SafeValue));
     delete [] d->data;
     d->data = newArray + d->offset;
     if (d->attrs) {
@@ -102,9 +102,9 @@ void ArrayData::reserve(ArrayData *d, uint n)
         return;
 
     d->alloc = qMax(n, 2*d->alloc);
-    Property *newArrayData = new Property[d->alloc + d->offset];
+    SafeValue *newArrayData = new SafeValue[d->alloc + d->offset];
     if (d->data) {
-        memcpy(newArrayData + d->offset, d->data, sizeof(Property)*d->len);
+        memcpy(newArrayData + d->offset, d->data, sizeof(SafeValue)*d->len);
         delete [] (d->data - d->offset);
     }
     d->data = newArrayData + d->offset;
@@ -144,14 +144,19 @@ ReturnedValue ArrayData::get(const ArrayData *d, uint index)
 {
     if (index >= d->len)
         return Primitive::emptyValue().asReturnedValue();
-    return d->data[index].value.asReturnedValue();
+    return d->data[index].asReturnedValue();
 }
 
 bool ArrayData::put(ArrayData *d, uint index, ValueRef value)
 {
-    Q_ASSERT(!d->attrs || !d->attrs->isAccessor());
+    Q_ASSERT(index >= d->len || !d->attrs || !d->attrs[index].isAccessor());
     // ### honour attributes
-    d->data[index].value = value;
+    d->data[index] = value;
+    if (index >= d->len) {
+        if (d->attrs)
+            d->attrs[index] = Attr_Data;
+        d->len = index;
+    }
     return true;
 }
 
@@ -161,12 +166,12 @@ bool ArrayData::del(ArrayData *d, uint index)
         return true;
 
     if (!d->attrs || d->attrs[index].isConfigurable()) {
-        d->data[index].value = Primitive::emptyValue();
+        d->data[index] = Primitive::emptyValue();
         if (d->attrs)
             d->attrs[index] = Attr_Data;
         return true;
     }
-    if (d->data[index].value.isEmpty())
+    if (d->data[index].isEmpty())
         return true;
     return false;
 }
@@ -192,7 +197,7 @@ void ArrayData::push_front(ArrayData *d, SafeValue *values, uint n)
         --d->data;
         ++d->len;
         ++d->alloc;
-        d->data->value = values[i].asReturnedValue();
+        *d->data = values[i].asReturnedValue();
     }
 
 }
@@ -203,7 +208,7 @@ ReturnedValue ArrayData::pop_front(ArrayData *d)
     if (!d->len)
         return Encode::undefined();
 
-    ReturnedValue v = d->data[0].value.isEmpty() ? Encode::undefined() : d->data[0].value.asReturnedValue();
+    ReturnedValue v = d->data[0].isEmpty() ? Encode::undefined() : d->data[0].asReturnedValue();
     ++d->offset;
     ++d->data;
     --d->len;
@@ -214,14 +219,14 @@ ReturnedValue ArrayData::pop_front(ArrayData *d)
 uint ArrayData::truncate(ArrayData *d, uint newLen)
 {
     if (d->attrs) {
-        Property *it = d->data + d->len;
-        const Property *begin = d->data + newLen;
+        SafeValue *it = d->data + d->len;
+        const SafeValue *begin = d->data + newLen;
         while (--it >= begin) {
-            if (!it->value.isEmpty() && !d->attrs[it - d->data].isConfigurable()) {
+            if (!it->isEmpty() && !d->attrs[it - d->data].isConfigurable()) {
                 newLen = it - d->data + 1;
                 break;
             }
-            it->value = Primitive::emptyValue();
+            *it = Primitive::emptyValue();
         }
     }
     d->len = newLen;
@@ -233,9 +238,9 @@ 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();
+        d->data[i] = Primitive::emptyValue();
     for (uint i = 0; i < n; ++i)
-        d->data[index + i].value = values[i];
+        d->data[index + i] = values[i];
     d->len = qMax(d->len, index + n);
     return true;
 }
@@ -244,8 +249,17 @@ 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;
+    SafeValue *v = dd->data + idx;
+    if (dd->attrs && dd->attrs[idx].isAccessor()) {
+        // double slot, free both. Order is important, so we have a double slot for allocation again afterwards.
+        v[1].tag = Value::Empty_Type;
+        v[1].uint_32 = dd->freeList;
+        v[0].tag = Value::Empty_Type;
+        v[0].uint_32 = idx + 1;
+    } else {
+        v->tag = Value::Empty_Type;
+        v->uint_32 = dd->freeList;
+    }
     dd->freeList = idx;
     if (dd->attrs)
         dd->attrs[idx].clear();
@@ -266,12 +280,13 @@ void SparseArrayData::reserve(ArrayData *d, uint n)
         return;
 
     SparseArrayData *dd = static_cast<SparseArrayData *>(d);
+    uint oldAlloc = dd->alloc;
     // ### FIXME
     dd->len = dd->alloc;
     dd->alloc = qMax(n, 2*dd->alloc);
-    Property *newArrayData = new Property[dd->alloc];
+    SafeValue *newArrayData = new SafeValue[dd->alloc];
     if (dd->data) {
-        memcpy(newArrayData, dd->data, sizeof(Property)*dd->len);
+        memcpy(newArrayData, dd->data, sizeof(SafeValue)*dd->len);
         delete [] dd->data;
     }
     dd->data = newArrayData;
@@ -281,21 +296,41 @@ void SparseArrayData::reserve(ArrayData *d, uint n)
         delete [] dd->attrs;
         dd->attrs = newAttrs;
     }
-    for (uint i = dd->freeList; i < dd->alloc; ++i)
-        dd->data[i].value = Primitive::fromInt32(i + 1);
+    for (uint i = oldAlloc; i < dd->alloc; ++i)
+        dd->data[i] = Primitive::fromInt32(i + 1);
 }
 
-uint SparseArrayData::allocate(ArrayData *d)
+// double slots are required for accessor properties
+uint SparseArrayData::allocate(ArrayData *d, bool doubleSlot)
 {
     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;
+    if (doubleSlot) {
+        uint *last = &dd->freeList;
+        while (1) {
+            if (*last + 1 >= dd->alloc) {
+                reserve(d, d->alloc + 2);
+                last = &dd->freeList;
+            }
+
+            if (dd->data[*last].uint_32 == (*last + 1)) {
+                // found two slots in a row
+                uint idx = *last;
+                *last = dd->data[*last + 1].uint_32;
+                d->attrs[idx] = Attr_Accessor;
+                return idx;
+            }
+            last = &dd->data[*last].uint_32;
+        }
+    } else {
+        if (dd->alloc == dd->freeList)
+            reserve(d, d->alloc + 2);
+        uint idx = dd->freeList;
+        dd->freeList = dd->data[idx].uint_32;
+        if (dd->attrs)
+            dd->attrs[idx] = Attr_Data;
+        return idx;
+    }
 }
 
 ReturnedValue SparseArrayData::get(const ArrayData *d, uint index)
@@ -303,16 +338,21 @@ 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();
+    return d->data[n->value].asReturnedValue();
 }
 
 bool SparseArrayData::put(ArrayData *d, uint index, ValueRef value)
 {
-    // ### honour attributes
+    if (value->isEmpty())
+        return true;
+
     SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index);
+    Q_ASSERT(n->value == UINT_MAX || !d->attrs || !d->attrs[n->value].isAccessor());
     if (n->value == UINT_MAX)
         n->value = allocate(d);
-    d->data[n->value].value = value;
+    d->data[n->value] = value;
+    if (d->attrs)
+        d->attrs[n->value] = Attr_Data;
     return true;
 }
 
@@ -324,22 +364,43 @@ bool SparseArrayData::del(ArrayData *d, uint index)
         return true;
 
     uint pidx = n->value;
-    Q_ASSERT(!dd->data[pidx].value.isEmpty());
+    Q_ASSERT(!dd->data[pidx].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;
+    bool isAccessor = false;
+    if (dd->attrs) {
+        if (!dd->attrs[pidx].isConfigurable())
+            return false;
+
+        isAccessor = dd->attrs[pidx].isAccessor();
+        dd->attrs[pidx] = Attr_Data;
     }
-    return false;
+
+    if (isAccessor) {
+        // free up both indices
+        d->data[pidx + 1].tag = Value::Undefined_Type;
+        d->data[pidx + 1].uint_32 = static_cast<SparseArrayData *>(d)->freeList;
+        d->data[pidx].tag = Value::Undefined_Type;
+        d->data[pidx].uint_32 = pidx + 1;
+    } else {
+        d->data[pidx].tag = Value::Undefined_Type;
+        d->data[pidx].uint_32 = static_cast<SparseArrayData *>(d)->freeList;
+    }
+
+    static_cast<SparseArrayData *>(d)->freeList = pidx;
+    static_cast<SparseArrayData *>(d)->sparse->erase(n);
+    return true;
 }
 
 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);
+        n->value = allocate(d, attrs.isAccessor());
+    else if (attrs.isAccessor() != d->attrs[n->value].isAccessor()) {
+        // need to convert the slot
+        free(d, n->value);
+        n->value = allocate(d, attrs.isAccessor());
+    }
     d->attrs[n->value] = attrs;
 }
 
@@ -356,7 +417,7 @@ 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];
+        d->data[idx] = values[i];
         static_cast<SparseArrayData *>(d)->sparse->push_front(idx);
     }
 }
@@ -367,7 +428,7 @@ ReturnedValue SparseArrayData::pop_front(ArrayData *d)
     uint idx = static_cast<SparseArrayData *>(d)->sparse->pop_front();
     ReturnedValue v;
     if (idx != UINT_MAX) {
-        v = d->data[idx].value.asReturnedValue();
+        v = d->data[idx].asReturnedValue();
         SparseArrayData::free(d, idx);
     } else {
         v = Encode::undefined();
@@ -381,16 +442,13 @@ uint SparseArrayData::truncate(ArrayData *d, uint 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;
+            free(d, it->value);
             bool brk = (it == begin);
             SparseArrayNode *prev = it->previousNode();
             static_cast<SparseArrayData *>(d)->sparse->erase(it);
@@ -415,7 +473,7 @@ uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
 {
     ArrayData *d = o->arrayData;
     if (!n)
-        return d->len;
+        return o->getLength();
 
     const ArrayData *other = otherObj->arrayData;
 
@@ -424,39 +482,37 @@ uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
         d = o->arrayData;
     }
 
-    uint oldSize = d->len;
+    uint oldSize = o->getLength();
 
     // ### 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;
+            if (otherObj->hasAccessorProperty && other->hasAttributes()) {
+                for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin();
+                     it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode())
+                    o->arraySet(oldSize + it->key(), *reinterpret_cast<Property *>(other->data + it->value), other->attrs[it->value]);
+            } else {
+                for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin();
+                     it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode())
+                    o->arraySet(oldSize + it->key(), other->data[it->value]);
             }
+        } else {
+            d->put(oldSize, other->data, n);
         }
     } 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();
+                d->data[i] = 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->data[oldSize + i] = 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();
+                    d->data[oldSize + i] = Primitive::emptyValue();
             }
         } else {
             d->len = oldSize + other->len;
@@ -469,95 +525,134 @@ uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
     return oldSize + n;
 }
 
-Property *ArrayData::insert(Object *o, uint index)
+Property *ArrayData::insert(Object *o, uint index, bool isAccessor)
 {
     Property *pd;
     if (o->arrayData->type != ArrayData::Sparse && (index < 0x1000 || index < o->arrayData->len + (o->arrayData->len >> 2))) {
+        Q_ASSERT(!isAccessor);
         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->data[i] = Primitive::emptyValue();
             o->arrayData->len = index + 1;
         }
-        pd = o->arrayData->data + index;
+        pd = reinterpret_cast<Property *>(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;
+            n->value = SparseArrayData::allocate(o->arrayData, isAccessor);
+        pd = reinterpret_cast<Property *>(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);
-            }
-        }
-    }
-
+    for (uint i = 0; i < len; ++i)
+        data[i].mark(e);
 }
 
 void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len)
 {
+    if (!len)
+        return;
+
     ArrayData *d = thisObject->arrayData;
-    if (!d || !d->len)
+    if (!d || (!d->len && d->type != ArrayData::Sparse))
         return;
 
-    if (d->type == ArrayData::Sparse) {
-        context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays"));
+    if (!(comparefn->isUndefined() || comparefn->asObject())) {
+        context->throwTypeError();
         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) {
+
+    if (d->type == ArrayData::Sparse) {
+        // since we sort anyway, we can simply iterate over the entries in the sparse
+        // array and append them one by one to a regular one.
+        SparseArrayData *sparse = static_cast<SparseArrayData *>(d);
+
+        if (!sparse->sparse->nEntries())
+            return;
+
+        thisObject->arrayData = new ArrayData;
+        d = thisObject->arrayData;
+        d->vtable->reserve(d, sparse->sparse->nEntries());
+
+        SparseArrayNode *n = sparse->sparse->begin();
+        uint i = 0;
+        if (sparse->attrs) {
+            d->ensureAttributes();
+            while (n != sparse->sparse->end()) {
+                if (n->value >= len)
+                    break;
+
+                PropertyAttributes a = sparse->attrs ? sparse->attrs[n->value] : Attr_Data;
+                d->data[i] = thisObject->getValue(reinterpret_cast<Property *>(sparse->data + n->value), a);
+                d->attrs[i] = a.isAccessor() ? Attr_Data : a;
+
+                n = n->nextNode();
+                ++i;
+            }
+        } else {
+            while (n != sparse->sparse->end()) {
+                if (n->value >= len)
+                    break;
+                d->data[i] = sparse->data[n->value];
+                n = n->nextNode();
+                ++i;
+            }
+        }
+        d->len = i;
+        if (len > i)
+            len = i;
+        if (n != sparse->sparse->end()) {
+            // have some entries outside the sort range that we need to ignore when sorting
+            thisObject->initSparseArray();
+            d = thisObject->arrayData;
+            while (n != sparse->sparse->end()) {
+                PropertyAttributes a = sparse->attrs ? sparse->attrs[n->value] : Attr_Data;
+                thisObject->arraySet(n->value, *reinterpret_cast<Property *>(sparse->data + n->value), a);
+
+                n = n->nextNode();
+            }
+
+        }
+
+        sparse->ArrayData::free();
+    } else {
+        if (len > d->len)
+            len = d->len;
+
+        // sort empty values to the end
         for (uint i = 0; i < len; i++) {
-            if (d->data[i].value.isEmpty()) {
+            if (d->data[i].isEmpty()) {
                 while (--len > i)
-                    if (!d->data[len].value.isEmpty())
+                    if (!d->data[len].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;
+                Q_ASSERT(!d->attrs || !d->attrs[len].isAccessor());
+                d->data[i] = d->data[len];
+                d->data[len] = Primitive::emptyValue();
             }
         }
-    }
 
-    if (!(comparefn->isUndefined() || comparefn->asObject())) {
-        context->throwTypeError();
-        return;
+        if (!len)
+            return;
     }
 
+
     ArrayElementLessThan lessThan(context, thisObject, comparefn);
 
-    if (!len)
-        return;
-    Property *begin = d->data;
+    SafeValue *begin = d->data;
     std::sort(begin, begin + len, lessThan);
+
+#ifdef CHECK_SPARSE_ARRAYS
+    thisObject->initSparseArray();
+#endif
+
 }
index 98968fa..3e09010 100644 (file)
@@ -95,7 +95,7 @@ struct Q_QML_EXPORT ArrayData
     uint alloc;
     uint type;
     PropertyAttributes *attrs;
-    Property *data;
+    SafeValue *data;
 
     bool isSparse() const { return this && type == Sparse; }
 
@@ -170,7 +170,7 @@ struct Q_QML_EXPORT ArrayData
 
     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);
+    static Property *insert(Object *o, uint index, bool isAccessor = false);
     void markObjects(ExecutionEngine *e);
 
     static void getHeadRoom(ArrayData *d);
@@ -200,7 +200,7 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData
     uint freeList;
     SparseArray *sparse;
 
-    static uint allocate(ArrayData *d);
+    static uint allocate(ArrayData *d, bool doubleSlot = false);
     static void free(ArrayData *d, uint idx);
 
     static void freeData(ArrayData *d);
@@ -224,14 +224,14 @@ inline Property *ArrayData::getProperty(uint index) const
     if (!this)
         return 0;
     if (type != Sparse) {
-        if (index >= len || data[index].value.isEmpty())
+        if (index >= len || data[index].isEmpty())
             return 0;
-        return data + index;
+        return reinterpret_cast<Property *>(data + index);
     } else {
         SparseArrayNode *n = static_cast<const SparseArrayData *>(this)->sparse->findNode(index);
         if (!n)
             return 0;
-        return data + n->value;
+        return reinterpret_cast<Property *>(data + n->value);
     }
 }
 
index eb8a530..4ea979d 100644 (file)
@@ -613,7 +613,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
 
     ScopedValue value(scope);
 
-    if ((instance->arrayData && instance->arrayType() != ArrayData::Simple) || instance->protoHasArray()) {
+    if (instance->hasAccessorProperty || (instance->arrayType() >= ArrayData::Sparse) || instance->protoHasArray()) {
         // lets be safe and slow
         for (uint i = fromIndex; i < len; ++i) {
             bool exists;
@@ -625,31 +625,22 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
         }
     } 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 {
+        Q_ASSERT(instance->arrayType() == ArrayData::Simple || instance->arrayType() == ArrayData::Complex);
         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));
+        SafeValue *val = instance->arrayData->data;
+        SafeValue *end = val + len;
+        val += fromIndex;
+        while (val < end) {
+            if (!val->isEmpty()) {
+                value = *val;
                 if (scope.hasException())
                     return Encode::undefined();
                 if (__qmljs_strict_equal(value, searchValue))
-                    return Encode((uint)(pd - instance->arrayData->data));
+                    return Encode((uint)(val - instance->arrayData->data));
             }
-            ++pd;
+            ++val;
         }
     }
     return Encode(-1);
index daa3d5b..858d625 100644 (file)
@@ -351,8 +351,8 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx)
                 callData->args[i] = arr->getIndexed(i);
         } else {
             int alen = qMin(len, arr->arrayData->length());
-            for (int i = 0; i < alen; ++i)
-                callData->args[i] = arr->arrayData->data[i].value;
+            if (alen)
+                memcpy(callData->args, arr->arrayData->data, alen*sizeof(SafeValue));
             for (quint32 i = alen; i < len; ++i)
                 callData->args[i] = Primitive::undefinedValue();
         }
index 4d02d46..f3a4b8a 100644 (file)
@@ -229,14 +229,9 @@ void Object::markObjects(Managed *that, ExecutionEngine *e)
     } else {
         for (uint i = 0; i < o->internalClass->size; ++i) {
             const Property &pd = o->memberData[i];
-            if (o->internalClass->propertyData[i].isAccessor()) {
-                if (pd.getter())
-                    pd.getter()->mark(e);
-                if (pd.setter())
-                    pd.setter()->mark(e);
-            } else {
-                pd.value.mark(e);
-            }
+            pd.value.mark(e);
+            if (o->internalClass->propertyData[i].isAccessor())
+                pd.set.mark(e);
         }
     }
     if (o->arrayData)
@@ -562,7 +557,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uin
             while (it->arrayNode != o->sparseEnd()) {
                 int k = it->arrayNode->key();
                 uint pidx = it->arrayNode->value;
-                Property *p = o->arrayData->data + pidx;
+                Property *p = reinterpret_cast<Property *>(o->arrayData->data + pidx);
                 it->arrayNode = it->arrayNode->nextNode();
                 PropertyAttributes a = o->arrayData->attributes(k);
                 if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
@@ -578,14 +573,14 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uin
         }
         // dense arrays
         while (it->arrayIndex < o->arrayData->length()) {
-            Property *p = o->arrayData->data + it->arrayIndex;
+            SafeValue *val = o->arrayData->data + it->arrayIndex;
             PropertyAttributes a = o->arrayData->attributes(it->arrayIndex);
             ++it->arrayIndex;
-            if (!p->value.isEmpty()
+            if (!val->isEmpty()
                 && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
                 *index = it->arrayIndex - 1;
                 *attrs = a;
-                *pd = *p;
+                pd->value = *val;
                 return;
             }
         }
@@ -874,6 +869,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name,
     Scope scope(ctx);
     Property *current;
     PropertyAttributes *cattrs;
+    uint memberIndex;
 
     if (isArrayObject() && name->equals(ctx->engine->id_length)) {
         assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length));
@@ -904,11 +900,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name,
     }
 
     // Clause 1
-    {
-        uint member = internalClass->find(name.getPointer());
-        current = (member < UINT_MAX) ? memberData + member : 0;
-        cattrs = internalClass->propertyData.constData() + member;
-    }
+    memberIndex = internalClass->find(name.getPointer());
+    current = (memberIndex < UINT_MAX) ? memberData + memberIndex : 0;
+    cattrs = internalClass->propertyData.constData() + memberIndex;
 
     if (!current) {
         // clause 3
@@ -921,7 +915,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name,
         return true;
     }
 
-    return __defineOwnProperty__(ctx, current, name, p, attrs);
+    return __defineOwnProperty__(ctx, memberIndex, name, p, attrs);
 reject:
   if (ctx->strictMode)
       ctx->throwTypeError();
@@ -972,24 +966,28 @@ bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Propert
         return true;
     }
 
-    return __defineOwnProperty__(ctx, current, StringRef::null(), p, attrs);
+    return __defineOwnProperty__(ctx, index, StringRef::null(), p, attrs);
 reject:
   if (ctx->strictMode)
       ctx->throwTypeError();
   return false;
 }
 
-bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs)
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const StringRef member, const Property &p, PropertyAttributes attrs)
 {
     // clause 5
     if (attrs.isEmpty())
         return true;
 
+    Property *current;
     PropertyAttributes cattrs;
-    if (!member.isNull())
-        cattrs = internalClass->propertyData[current - memberData];
-    else
-        cattrs = arrayData->attrs ? arrayData->attrs[current - arrayData->data] : Attr_Data;
+    if (!member.isNull()) {
+        current = memberData + index;
+        cattrs = internalClass->propertyData[index];
+    } else {
+        current = arrayData->getProperty(index);
+        cattrs = arrayData->attributes(index);
+    }
 
     // clause 6
     if (p.isSubset(attrs, *current, cattrs))
@@ -1016,12 +1014,23 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
             // 9b
             cattrs.setType(PropertyAttributes::Accessor);
             cattrs.clearWritable();
+            if (member.isNull()) {
+                // need to convert the array and the slot
+                initSparseArray();
+                arrayData->setAttributes(index, cattrs);
+                current = arrayData->getProperty(index);
+            }
             current->setGetter(0);
             current->setSetter(0);
         } else {
             // 9c
             cattrs.setType(PropertyAttributes::Data);
             cattrs.setWritable(false);
+            if (member.isNull()) {
+                // need to convert the array and the slot
+                arrayData->setAttributes(index, cattrs);
+                current = arrayData->getProperty(index);
+            }
             current->value = Primitive::undefinedValue();
         }
     } else if (cattrs.isData() && attrs.isData()) { // clause 10
@@ -1030,11 +1039,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
                 goto reject;
         }
     } else { // clause 10
-        assert(cattrs.isAccessor() && attrs.isAccessor());
+        Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor());
         if (!cattrs.isConfigurable()) {
-            if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1)))
+            if (!p.value.isEmpty() && current->value.val != p.value.val)
                 goto reject;
-            if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1)))
+            if (!p.set.isEmpty() && current->set.val != p.set.val)
                 goto reject;
         }
     }
@@ -1045,9 +1054,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
     if (!member.isNull()) {
         internalClass = internalClass->changeMember(member.getPointer(), cattrs);
     } else {
-        arrayData->setAttributes(current - arrayData->data, cattrs);
+        arrayData->setAttributes(index, cattrs);
     }
-    if (attrs.isAccessor())
+    if (cattrs.isAccessor())
         hasAccessorProperty = 1;
     return true;
   reject:
@@ -1078,6 +1087,13 @@ void Object::copyArrayData(Object *other)
         for (uint i = 0; i < len; ++i) {
             arraySet(i, (v = other->getIndexed(i)));
         }
+    } else if (!other->arrayData) {
+        ;
+    } else if (other->hasAccessorProperty && other->arrayData->attrs && other->arrayData->isSparse()){
+        // do it the slow way
+        for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other->arrayData)->sparse->begin();
+             it != static_cast<const SparseArrayData *>(other->arrayData)->sparse->end(); it = it->nextNode())
+            arraySet(it->key(), *reinterpret_cast<Property *>(other->arrayData->data + it->value), other->arrayData->attrs[it->value]);
     } else {
         Q_ASSERT(!arrayData && other->arrayData);
         if (other->arrayType() == ArrayData::Sparse) {
@@ -1087,11 +1103,11 @@ void Object::copyArrayData(Object *other)
             dd->sparse = new SparseArray(*od->sparse);
             dd->freeList = od->freeList;
             arrayData = dd;
+            other->arrayData->len = other->arrayData->alloc;
         }
         arrayReserve(other->arrayData->len);
         arrayData->len = other->arrayData->len;
-        // ### correctly deal with accessor properties
-        memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(Property));
+        memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(SafeValue));
 
         arrayData->offset = 0;
     }
@@ -1152,21 +1168,25 @@ void Object::initSparseArray()
     uint *lastFree = &data->freeList;
     for (uint i = 0; i < oldOffset; ++i) {
         *lastFree = i;
-        lastFree = &data->data[i].value.uint_32;
+        data->data[i].tag = Value::Empty_Type;
+        lastFree = &data->data[i].uint_32;
     }
     for (uint i = 0; i < data->len; ++i) {
-        if (!data->data[i + oldOffset].value.isEmpty()) {
+        if (!data->data[i + oldOffset].isEmpty()) {
             SparseArrayNode *n = data->sparse->insert(i);
             n->value = i + oldOffset;
         } else {
             *lastFree = i + oldOffset;
-            lastFree = &data->data[i + oldOffset].value.uint_32;
+            data->data[i + oldOffset].tag = Value::Empty_Type;
+            lastFree = &data->data[i + oldOffset].uint_32;
         }
     }
     for (uint i = data->len + oldOffset; i < data->alloc; ++i) {
         *lastFree = i;
-        lastFree = &data->data[i].value.uint_32;
+        data->data[i].tag = Value::Empty_Type;
+        lastFree = &data->data[i].uint_32;
     }
+    *lastFree = data->alloc;
     arrayData = data;
 }
 
index f99191a..de44d5c 100644 (file)
@@ -136,7 +136,7 @@ struct Q_QML_EXPORT Object: Managed {
     bool hasOwnProperty(const StringRef name) const;
     bool hasOwnProperty(uint index) const;
 
-    bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs);
+    bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const StringRef member, const Property &p, PropertyAttributes attrs);
     bool __defineOwnProperty__(ExecutionContext *ctx, const StringRef name, const Property &p, PropertyAttributes attrs);
     bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs);
     bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs);
@@ -209,17 +209,15 @@ public:
     void arrayCreate() {
         if (!arrayData)
             arrayData = new ArrayData;
+#ifdef CHECK_SPARSE_ARRAYS
+        initSparseArray();
+#endif
     }
 
     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; }
 
-    inline Property *arrayInsert(uint index) {
-        arrayCreate();
-        return ArrayData::insert(this, index);
-    }
-
     inline bool protoHasArray() {
         Scope scope(engine());
         Scoped<Object> p(scope, this);
@@ -344,7 +342,6 @@ inline void Object::setArrayLengthUnchecked(uint l)
 inline void Object::push_back(const ValueRef v)
 {
     arrayCreate();
-    Q_ASSERT(!arrayData->isSparse());
 
     uint idx = getLength();
     arrayReserve(idx + 1);
@@ -355,12 +352,21 @@ inline void Object::push_back(const ValueRef v)
 
 inline void Object::arraySet(uint index, const Property &p, PropertyAttributes attributes)
 {
-    if (attributes.isAccessor())
+    // ### Clean up
+    arrayCreate();
+    if (attributes.isAccessor()) {
         hasAccessorProperty = 1;
-
-    Property *pd = arrayInsert(index);
-    *pd = p;
+        initSparseArray();
+    } else if (index > 0x1000 && index > 2*arrayData->alloc) {
+        initSparseArray();
+    } else {
+        arrayData->vtable->reserve(arrayData, index + 1);
+    }
     arrayData->setAttributes(index, attributes);
+    Property *pd = ArrayData::insert(this, index, attributes.isAccessor());
+    pd->value = p.value;
+    if (attributes.isAccessor())
+        pd->set = p.set;
     if (isArrayObject() && index >= getLength())
         setArrayLengthUnchecked(index + 1);
 }
@@ -368,7 +374,11 @@ inline void Object::arraySet(uint index, const Property &p, PropertyAttributes a
 
 inline void Object::arraySet(uint index, ValueRef value)
 {
-    Property *pd = arrayInsert(index);
+    arrayCreate();
+    if (index > 0x1000 && index > 2*arrayData->alloc) {
+        initSparseArray();
+    }
+    Property *pd = ArrayData::insert(this, index);
     pd->value = value ? *value : Primitive::undefinedValue();
     if (isArrayObject() && index >= getLength())
         setArrayLengthUnchecked(index + 1);
index 410f243..1096aa1 100644 (file)
@@ -597,8 +597,8 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, const ValueRef
     }
 
     attrs->clear();
-    desc->setGetter(0);
-    desc->setSetter(0);
+    desc->value = Primitive::emptyValue();
+    desc->set = Primitive::emptyValue();
     ScopedValue tmp(scope);
 
     if (o->hasProperty(ctx->engine->id_enumerable))
@@ -610,10 +610,8 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, const ValueRef
     if (o->hasProperty(ctx->engine->id_get)) {
         ScopedValue get(scope, o->get(ctx->engine->id_get));
         FunctionObject *f = get->asFunctionObject();
-        if (f) {
-            desc->setGetter(f);
-        } else if (get->isUndefined()) {
-            desc->setGetter((FunctionObject *)0x1);
+        if (f || get->isUndefined()) {
+            desc->value = get;
         } else {
             ctx->throwTypeError();
             return;
@@ -624,10 +622,8 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, const ValueRef
     if (o->hasProperty(ctx->engine->id_set)) {
         ScopedValue set(scope, o->get(ctx->engine->id_set));
         FunctionObject *f = set->asFunctionObject();
-        if (f) {
-            desc->setSetter(f);
-        } else if (set->isUndefined()) {
-            desc->setSetter((FunctionObject *)0x1);
+        if (f || set->isUndefined()) {
+            desc->set = set;
         } else {
             ctx->throwTypeError();
             return;
index 4b0896b..6381fe7 100644 (file)
@@ -62,10 +62,10 @@ struct Property {
         }
         if (attrs->type() == PropertyAttributes::Accessor) {
             attrs->clearWritable();
-            if (value.managed() == (Managed *)0x1)
-                value = Primitive::fromManaged(0);
-            if (set.managed() == (Managed *)0x1)
-                set = Primitive::fromManaged(0);
+            if (value.isEmpty())
+                value = Primitive::undefinedValue();
+            if (set.isEmpty())
+                set = Primitive::undefinedValue();
         }
         attrs->resolve();
     }
@@ -91,8 +91,8 @@ struct Property {
     inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const;
     inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs);
 
-    inline FunctionObject *getter() const { return reinterpret_cast<FunctionObject *>(value.managed()); }
-    inline FunctionObject *setter() const { return reinterpret_cast<FunctionObject *>(set.managed()); }
+    inline FunctionObject *getter() const { return reinterpret_cast<FunctionObject *>(value.asManaged()); }
+    inline FunctionObject *setter() const { return reinterpret_cast<FunctionObject *>(set.asManaged()); }
     inline void setGetter(FunctionObject *g) { value = Primitive::fromManaged(reinterpret_cast<Managed *>(g)); }
     inline void setSetter(FunctionObject *s) { set = Primitive::fromManaged(reinterpret_cast<Managed *>(s)); }
 };
@@ -110,9 +110,9 @@ inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &
     if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value))
         return false;
     if (attrs.type() == PropertyAttributes::Accessor) {
-        if (value.managed() != other.value.managed())
+        if (value.asManaged() != other.value.asManaged())
             return false;
-        if (set.managed() != other.set.managed())
+        if (set.asManaged() != other.set.asManaged())
             return false;
     }
     return true;
@@ -128,10 +128,10 @@ inline void Property::merge(PropertyAttributes &attrs, const Property &other, Pr
         attrs.setWritable(otherAttrs.isWritable());
     if (otherAttrs.type() == PropertyAttributes::Accessor) {
         attrs.setType(PropertyAttributes::Accessor);
-        if (other.value.managed())
-            value = (other.value.managed() == (Managed *)0x1) ? Primitive::fromManaged(0).asReturnedValue() : other.value.asReturnedValue();
-        if (other.set.managed())
-            set = (other.set.managed() == (Managed *)0x1) ? Primitive::fromManaged(0).asReturnedValue() : other.set.asReturnedValue();
+        if (!other.value.isEmpty())
+            value = other.value;
+        if (!other.set.isEmpty())
+            set = other.set;
     } else if (otherAttrs.type() == PropertyAttributes::Data){
         attrs.setType(PropertyAttributes::Data);
         value = other.value;
index 4c7ec66..a8b4533 100644 (file)
@@ -1120,8 +1120,8 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const ValueRef
 
     uint idx = name->asArrayIndex();
     Property pd;
-    pd.setGetter(getter ? getter->asFunctionObject() : 0);
-    pd.setSetter(setter ? setter->asFunctionObject() : 0);
+    pd.value = getter;
+    pd.set = setter;
     if (idx != UINT_MAX) {
         o->arraySet(idx, pd, Attr_Accessor);
     } else {
@@ -1139,9 +1139,9 @@ ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx,
         if (klass->propertyData[i].isData())
             o->memberData[i].value = *args++;
         else {
-            o->memberData[i].setGetter(args->asFunctionObject());
+            o->memberData[i].value = *args;
             args++;
-            o->memberData[i].setSetter(args->asFunctionObject());
+            o->memberData[i].set = *args;
             args++;
         }
     }
index 97dd695..7169f5c 100644 (file)
 
 using namespace QV4;
 
-bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const
+bool ArrayElementLessThan::operator()(const SafeValue &v1, const SafeValue &v2) const
 {
     Scope scope(m_context);
 
-    if (p1.value.isUndefined() || p1.value.isEmpty())
+    if (v1.isUndefined() || v1.isEmpty())
         return false;
-    if (p2.value.isUndefined() || p2.value.isEmpty())
+    if (v2.isUndefined() || v2.isEmpty())
         return true;
     ScopedObject o(scope, m_comparefn);
     if (o) {
@@ -67,14 +67,14 @@ bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) co
         ScopedValue result(scope);
         ScopedCallData callData(scope, 2);
         callData->thisObject = Primitive::undefinedValue();
-        callData->args[0] = p1.value;
-        callData->args[1] = p2.value;
+        callData->args[0] = v1;
+        callData->args[1] = v2;
         result = __qmljs_call_value(m_context, m_comparefn, callData);
 
         return result->toNumber() < 0;
     }
-    ScopedString p1s(scope, p1.value.toString(m_context));
-    ScopedString p2s(scope, p2.value.toString(m_context));
+    ScopedString p1s(scope, v1.toString(m_context));
+    ScopedString p2s(scope, v2.toString(m_context));
     return p1s->toQString() < p2s->toQString();
 }
 
index f2ad167..6c2808b 100644 (file)
@@ -68,7 +68,7 @@ public:
     inline ArrayElementLessThan(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn)
         : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {}
 
-    bool operator()(const Property &v1, const Property &v2) const;
+    bool operator()(const SafeValue &v1, const SafeValue &v2) const;
 
 private:
     ExecutionContext *m_context;
index dfd093c..b9ec420 100644 (file)
@@ -140,7 +140,7 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, StringRef nam
     name = (String *)0;
     StringObject *s = static_cast<StringObject *>(m);
     uint slen = s->value.stringValue()->toQString().length();
-    if (it->arrayIndex < slen) {
+    if (it->arrayIndex <= slen) {
         while (it->arrayIndex < slen) {
             *index = it->arrayIndex;
             ++it->arrayIndex;
index 921a750..3d7adfe 100644 (file)
@@ -1,10 +1,16 @@
 var max
 for (max = 2; max < 100; ++max) {
     var arr = [];
-    arr[10000000] = -1
+    // force a sparse array
+    Object.defineProperty(arr, "0", {
+        get: function () {
+            return 0;
+        },
+        configurable: true
+    });
     var i;
     var j;
-    for (i = 0; i < max; ++i)
+    for (i = 1; i < max; ++i)
         arr[i] = i;
     for (i = 1; i < max; i += 2) {
         delete arr[i];
@@ -19,3 +25,31 @@ for (max = 2; max < 100; ++max) {
         }
     }
 }
+
+for (max = 2; max < 100; ++max) {
+    var arr = [];
+    // force a sparse array
+    Object.defineProperty(arr, "0", {
+        get: function () {
+            return 0;
+        },
+        configurable: true
+    });
+
+    var i;
+    var j;
+    for (i = 1; i < max; ++i)
+        arr[i] = i;
+    for (i = 0; i < max; i += 2) {
+        delete arr[i];
+        for (j = 0; j < max; ++j) {
+            if (j <= i && !(j %2)) {
+                if (arr[j] != undefined)
+                    throw "err1 " + i + " " + j + " " + arr[j]
+            } else {
+                if (arr[j] != j)
+                    throw "err2 " + j
+            }
+        }
+    }
+}